[PATCH] manual: Recommendations for dynamic linker hardening

Joe Simmons-Talbott josimmon@redhat.com
Fri May 10 15:49:43 GMT 2024


Some minor typos and such are pointed out below.

Thanks,
Joe

On Fri, May 10, 2024 at 10:11 AM Florian Weimer <fweimer@redhat.com> wrote:
>
> This new section in the manual provides recommendations for
> use of glibc in environments with higher integrity requirements.
> It's reflecting both current implementation shortcomings, and
> challenges we inherit from ELF and psABI requirements.
>
> ---
>  manual/dynlink.texi | 447 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 447 insertions(+)
>
> diff --git a/manual/dynlink.texi b/manual/dynlink.texi
> index d71f7a30d6..747e2c0030 100644
> --- a/manual/dynlink.texi
> +++ b/manual/dynlink.texi
> @@ -15,6 +15,7 @@ Dynamic linkers are sometimes called @dfn{dynamic loaders}.
>  @menu
>  * Dynamic Linker Invocation::   Explicit invocation of the dynamic linker.
>  * Dynamic Linker Introspection::    Interfaces for querying mapping information.
> +* Dynamic Linker Hardening::    Avoiding unexpected issues with dynamic linking.
>  @end menu
>
>  @node Dynamic Linker Invocation
> @@ -535,6 +536,452 @@ information is processed.
>  This function is a GNU extension.
>  @end deftypefun
>
> +@node Dynamic Linker Hardening
> +@section Avoiding Unexpected Issues With Dynamic Linking
> +
> +This section details recommendations for increasing application
> +robustness, by avoiding potential issues related to dynamic linking.
> +The recommendations have two main aims: reduce the involvement of the
> +dynamic linker in application execution after process startup, and
> +restrict the application to a dynamic linker feature set whose behavior
> +is more easily understood.
> +
> +Key aspects of limiting dynamic linker usage after startup are: no use
> +of the @code{dlopen} function, disabling lazy binding, and using the
> +static TLS model.  More easily understood dynamic linker behavior
> +requires avoiding name conflicts (symbols and sonames) and highly
> +customizable features like the audit subsystem.
> +
> +Note that while these steps can be considered a form of application
> +hardening, they do not guard against potential harm from accidental or
> +deliberate loading of untrusted or malicious code.
> +
> +@subsection Restricted Dynamic Linker Features
> +
> +Avoiding certain dynamic linker features can increase predictability of
> +applications and reduce the risk of running into dynamic linker defects.
> +
> +@itemize @bullet
> +@item
> +Do not use the functions @code{dlopen}, @code{dlmopen}, or
> +@code{dlclose}.  Dynamic loading and unloadign of shared objects

unloading

> +introduces substantial complications related to symbol and thread-local
> +storage (TLS) management.
> +
> +@item
> +Without the @code{dlopen} function, @code{dlsym} and @code{dlvsym}
> +cannot be used with shared object handles.  Minimizing use of both
> +functions is recommended.  If they have to be used, only the
> +@code{RTLD_DEFAULT} pseudo-handle should be used.
> +
> +@item
> +Use the local-exec or initial-exec TLS models.  If @code{dlopen} is not
> +used, there are no compatibility concerns for initial-exec TLS.  This
> +TLS model avoids most of the complexity around TLS access.  In
> +particular, there are no run-time memory allocations after process or
> +thread start.
> +
> +If shared objects are expected to be used more generally, outside the
> +restricted context, lack of compatibility between @code{dlopen} and
> +initial-exec TLS could be a concern.  In that case, the second-best
> +alternative is to use global-dynamic TLS with GNU2 TLS descriptors, for
> +targets that fully implement them, including the fast path for access to
> +TLS variables defined in the initially loaded set of objects.  Like
> +initial-exec TLS, this avoids memory allocations after thread creation.
> +
> +@item
> +Do not use lazy binding.  Lazy binding may require run-time memory
> +allocation, is not async-signal-safe, and introduces considerable
> +complexity.
> +
> +@item
> +Limit the use of indirect function (IFUNC) resolvers.  These resolvers
> +run during relocation processing, when @theglibc{} is not in a fully
> +consistent state.  If you use IFUNC resolvers, do not depend on external
> +data or function references.
> +
> +@item
> +Do not use the audit functionality (@code{LD_AUDIT}, @code{DT_AUDIT},
> +@code{DT_DEPAUDIT}).  Its callback and hooking capabilities introduce a
> +lot of complexity and subtly alter dynamic linker behavior in corner

subtlety

> +cases even if the audit module is inactive.
> +
> +@item
> +Do not use on symbol interposition, except in cases where copy

Do not use symbol interposition.

> +relocations are involved.  Without symbol interposition, the exact order
> +in which shared objects are searched are less relevant.
> +
> +@item
> +One potential source of symbol interposition is a combination of static
> +and dynamic linking, namely linking a static archive into multiple
> +dynamic shared objects.  For such scenarios, the static library should
> +be converted into its own dynamic shared object.
> +
> +A different approach to this situation uses hidden visibility for
> +symbols in the static library, but this can cause problems if the
> +library does not expect that multiple copies of its code coexist within
> +the same process, with no or partial sharing of state.
> +
> +@item
> +If you use shared objects that are linked with @code{-Wl,-Bsymbolic} (or
> +equivalent) or use protected visibility, them code for the main program

s/them/then/

> +must be built as @code{-fpic} to avoid creating copy relocations (and
> +the main program must not use copy relocations for other reasons).
> +
> +@item
> +Be careful about explicit section annotations.  Make sure that the
> +target section matches the properties of the declared entity (e.g., no
> +writable objects in @code{.text}).
> +
> +@item
> +Ensure that all assembler or object input files have recommended

have the recommended

> +security markup, particularly for non-executable stack.
> +
> +@item
> +Some features of @theglibc{} indirectly depend on run-time code loading
> +and @code{dlopen}.  Use @code{iconv_open} with built-in converters only
> +(such as @code{UTF-8}).  Do not use NSS functionality such as
> +@code{getaddrinfo} or @code{getpwuid_r} unless the system is configured
> +for built-in NSS service moduleso only (see below).

modules

> +@end itemize
> +
> +Several considerations apply to ELF constructors and destructors.
> +
> +@itemize @bullet
> +@item
> +The dynamic linker does not take constructor and destructor priorities
> +into account when determining their execution order.  Priorities are
> +only used by the link editor for ordering execution within a completely
> +linked object.
> +
> +@item
> +The recommendations to avoid cyclic dependencies and symbol
> +interposition (and the next two recommendations) make it less likely
> +that ELF objects are accessed before their ELF constructors have run (or
> +after their ELF destructors have run).  However, using @code{dlsym} and
> +@code{dlvsym}, it still possible to access uninitialized facilities even

it is still

> +with these restrictions in place.  Such access is also possible within a
> +single shared object (or the main executable).  Consider using dynamic,
> +on-demand initialization instead.  To deal with access after
> +de-initialization, it may be necessary to implement special cases for
> +that scenario, potentially with degraded functionality.
> +
> +@item
> +Do not use the @code{DT_PREINIT_ARRAY} dynamic tag.
> +
> +@item
> +Do not flag objects as @code{DF_1_INITFIRST}.
> +
> +@item
> +If @code{dlopen} and @code{dlmopen} are not used, @code{DT_NEEDED}
> +dependency information is complete, and lazy binding is disabled, the
> +execution order of ELF destructors is expected to be the reverse of the
> +ELF constructor order.  However, two separate dependency sort operations
> +still occur.  Even though the listed preconditions should ensure that
> +both sorts produce the same ordering, it is recommended not to depend on
> +the destructor order being the reverse of the constructor order.
> +@end itemize
> +
> +The following items provide C++-specific guidance for preparing
> +applications.  If another programming language is used and it uses these
> +toolchain features targeted at C++ to implement some language
> +constructs, these restrictions and recommendations still apply in
> +analogous ways.
> +
> +@itemize @bullet
> +@item
> +C++ inline functions, templates, and other constructs may need to be
> +duplicated into multiple shared objects, resulting in symbol
> +interposition.  (This scenario is similar to combining static and
> +dynamic linking, as discussed above.)
> +
> +It may be possible to use explicit template instantiations to avoid
> +symbol interposition arising from multiple copies of the same template.
> +Hidden visibility could be used as well, but this may cause problems if
> +the duplicated functionality cannot deal with multiple copies within the
> +same process, with no or only partial state sharing.
> +
> +In general, due to the way certain C++ features are implemented in the
> +toolchain, avoiding symbol interposition entirely may not be possible as
> +long as these C++ features are used.
> +
> +@item
> +The toolchain and dynamic linker have multiple mechanisms that bypass
> +the usual symbol binding procedures.  This means that the C++ one
> +definition rule (ODR) still holds even if certain symbol-based isolation
> +mechanisms are used, and object addresses are not shared across
> +translation units with incompatible type definitions.  This does not
> +matter if the previous advice regarding symbol interposition is
> +followed.  However, as the advice may be difficult to implement, it is
> +necessary to avoid ODR violations across the entire process image.
> +
> +@item
> +Be a ware that as a special of interposed symbols, symbols with the

aware

> +@code{STB_GNU_UNIQUE} type do not follow the usual symbol name space

namespace

> +isolation rules: such symbols bind across @code{RTLD_LOCAL} and
> +@code{dlmopen} boundaries.  Furthermore, symbol versioning is ignored
> +for such symbols; they are bound by symbol name only.  All their
> +definitions and uses must therefore be compatible.  Hidden visibility
> +still prevents the creation of @code{STB_GNU_UNIQUE} symbols and can
> +achieve isolation of incompatible definitions.
> +
> +@item
> +C++ exception handling and run-time type information (RTTI), as
> +implemented in the GNU toolchain, is not address-significant, and
> +therefore is not affected by the symbol binding behaviour of the dynamic
> +linker.  This means that types of the same fully-qualified name (in
> +non-anonymous name spaces) are always considered the same from an

namespaces

> +exception-handling or RTTI perspective.  This is true if the type
> +information object or vtable has hidden symbol visibility, or the
> +corresponding symbols are versioned under different symbol versions, or
> +the symbols not bound to the same objects due to the use of
> +@code{RTLD_LOCAL} or @code{dlmopen}.
> +
> +This can cause issues in applications that contain multiple incompatible
> +definitions of the same type.
> +
> +@item
> +C++ exception handling across multiple @code{dlmopen} name spaces may
> +not work, particular with the unwinder in GCC versions before 12.
> +Current toolchain versions are able to process unwinding tables across
> +@code{dlmopen} boundaries.  However, note that type comparison is
> +name-based, not address-based (see the previous item), so exception
> +types may still be matched in unexpected ways.  An important special
> +case of exception handling, invoking destructors for variables of block
> +scope, is not impacted by this RTTI type-sharing.  Likewise, regular
> +virtual member function dispatch for objects is unaffected (but still
> +requires that the type definitions match in all directly involved
> +translation units).
> +
> +@item
> +The Itanium C++ ABI requires that in some cases, C++ destructors for
> +global objects that are constructed on demand are not destructed in the
> +opposite order of their construction.  (The C++ standard requires
> +opposite constructor order.)  As a result, do not depend on the precise
> +destructor invocation order.
> +
> +@item
> +Registering destructors for later invocation allocates memory and may
> +result in process termination if insufficient memory is available.  In
> +particular, this applies to dynamic initialization of a block variable
> +with static storage duration of a type that has a non-trivial destructor.
> +If unexpected program termination is a concern, ensure that such
> +objects merely have trivial destructors, avoiding the need for
> +registration.
> +
> +This includes @code{thread_local} objects with destructors.  Callbacks
> +for destruction of per-thread objects can be registered using
> +@code{pthread_key_create} in a way that permits handling memory
> +allocation errors.
> +@end itemize
> +
> +
> +@subsection Producing Matching Binaries
> +
> +This subsection recommends tools and build flags for producing
> +applications that meet the recommendations of the previous subsection.
> +
> +@itemize @bullet
> +@item
> +Use BFD ld (@command{bfd.ld}) from GNU binutils to produce binaries,
> +invoked through a compiler driver such as @command{gcc}.  The version
> +should be not too far ahead of what was current when the version of
> +@theglibc{} was first released.
> +
> +@item
> +Do not use a binutils release that is older than the one used to build
> +@theglibc{} itself.
> +
> +@item
> +Compile with @code{-ftls-model=initial-exec} to force the initial-exec
> +TLS model.
> +
> +@item
> +Link with @option{-Wl,-z,now} to disable lazy binding.
> +
> +@item
> +Link with @option{-Wl,-z,relro} to enable RELRO (which is the default on
> +most targets).
> +
> +@item
> +Specify all direct shared objects dependencies using @code{-l} options
> +to avoid underlinking.  Rely on @code{.so} files (which can be linker
> +scripts) and searching with the @option{-l} option.  Do not specify the
> +file names of shared objects on the linker command line.
> +
> +@item
> +Consider using @option{-Wl,-z,defs} to treat underlinking as an error
> +condition.
> +
> +@item
> +For a shared object (linked with @option{-shared}, use
> +@option{-Wl,-soname,lib@dots{}} to set a soname that matches the final
> +installed name of the file.
> +
> +@item
> +Do not use the @option{-rpath} linker option.
> +
> +@item
> +Use @option{-Wl,--error-rwx-segments} and @option{-Wl,--error-execstack} to
> +instruct the link editor to fail the link if the resulting final object
> +would have read-write-execute segments or an executable stack.  Such
> +issues usually indicate that the input files are not marked up
> +correctly.
> +
> +@item
> +Ensure that for each @code{LOAD} segment in the ELF program header, file
> +offsets, memory sizes, and load addresses are multiples of the largest
> +page size supported at run time.  Similarly, the start address and size
> +of the @code{GNU_RELRO} range should be multiples of the page size.
> +
> +Avoid creating gaps between @code{LOAD} segments.  The difference
> +between the load addresses of two subsequent @code{LOAD} segments should
> +be the size of the first @code{LOAD} segment.  (This may require linking
> +with @option{-Wl,-z,noseparatecode}.)
> +
> +This may not be possible to achieve with currently available link

with the currently

> +editors.
> +
> +@item
> +If the multiple-of-page-size criterion for the @code{GNU_RELRO} region
> +cannot be achieved, ensure that the process memory image right before
> +the start of the region does not contain executable of writable memory.

executable or writable

> +@c https://sourceware.org/pipermail/libc-alpha/2022-May/138638.html
> +@end itemize
> +
> +@subsection Checking Binaries
> +
> +In some cases, if the previous recommendations are not followed, this
> +can be determined from the produced binaries.  This section contains
> +suggestions for verifying aspects of these binaries.
> +
> +@itemize @bullet
> +@item
> +To detect underlinking, examine the dynamic symbol table, for example
> +using @samp{readelf -sDW}.  If the symbol is defined in a shared object
> +that uses symbol versioning, it must carry a symbol version, as in
> +@samp{pthread_kill@@GLIBC_2.34}.
> +
> +@item
> +Examine the dynamic segment with @samp{readelf -dW} to check that all
> +the required @code{NEEDED} entries are present.  (It is not necessary to
> +list indirect dependencies.)
> +
> +@item
> +The @code{NEEDED} entries should not contain full path names including
> +slashes, only @code{sonames}.
> +
> +@item
> +For a further consistency check, collect all shared objects referenced
> +via @code{NEEDED} entries in dynamic segments, transitively, starting at
> +the main program.  Then determine their dynamic symbol tables (using
> +@samp{readelf -sDW}, for example).
> +
> +Ideally, every symbol should be defined at most once.
> +
> +If there are interposed data symbols, check if the single interposing
> +definition is in the main program.  In this case, there must be a copy
> +relocation for it.  (This only applies to targets with copy relocations.)
> +
> +Function symbols should never be interposed.
> +
> +@item
> +Using the previously collected @code{NEEDED} entries, check that the
> +dependency graph does not contain any cycles.
> +
> +@item
> +The dynamic segment should also mention @code{BIND_NOW} on the
> +@code{FLAGS} line or @code{NOW} on the @code{FLAGS_1} line (one is
> +enough).
> +
> +@item
> +For shared objects (not main programs), if the program header has a
> +@code{PT_TLS} segment, the dynamic segment (as shown by @samp{readelf
> +-dW}) should contain the @code{STATIC_TLS} flag on the @code{FLAGS}
> +line.
> +
> +If @code{STATIC_TLS} is missing in shared objects, ensure that the
> +appropriate relocations for GNU2 TLS descriptors are used (for example,
> +@code{R_AARCH64_TLSDESC} or @code{R_X86_64_TLSDESC}).
> +
> +@item
> +There should not be a reference to the @code{__tLs_get_addr} symbol in

__tls_get_addr?

> +the dynamic symbol table (in the @samp{readelf -sDW} output).
> +Thread-local storage must be accessed using the initial-exec (static)
> +model, or using GNU2 TLS descriptors.
> +
> +@item
> +Likewise, the functions @code{dlopen}, @code{dlmopen}, @code{dlclose}
> +should not be referenced from the dynamic symbol table.
> +
> +@item
> +For shared objects, there should be a @code{SONAME} entry that matches
> +the file name (the base name, i.e., the part after the slash).  The
> +@code{SONAME} string must not contain a slash @samp{/}.
> +
> +@item
> +For all objects, the dynamic segment (as shown by @samp{readelf -dW})
> +should not contain @code{RPATH} or @code{RUNPATH} entries.
> +
> +@item
> +Likewise, the dynamic segment should not show any @code{AUDIT},
> +@code{DEPAUDIT}, @code{AUXILIARY}, @code{FILTER}, or
> +@code{PREINIT_ARRAY} tags.
> +
> +@item
> +The @code{DF_1_INITFIRST} flag should not be used.
> +
> +@item
> +The program header must not have @code{LOAD} segments that are writable
> +and executable at the same time.
> +
> +@item
> +All produces objects should have @code{GNU_STACK} program header that is

produced

> +not marked as executable.  (However, on some newer targets, a
> +non-executable stack is the default, so the @code{GNU_STACK} program
> +header is not required.)
> +@end itemize
> +
> +@subsection Run-time Considerations
> +
> +@itemize @bullet
> +@item
> +Install shared objects using their sonames in a default search path
> +directory (usually @file{/usr/lib64}).  Do not use symbolic links.
> +@c This is currently not standard practice.
> +
> +@item
> +The default search path must not contain objects with duplicate file
> +names or sonames.
> +
> +@item
> +Do not use environment variables (@code{LD_@dots{} variables such as
> +@code{LD_PRELOAD} or @code{LD_LIBRARY_PATH}}, or @code{GLIBC_TUNABLES})
> +to change default dynamic linker behavior.
> +
> +@item
> +Do not install shared objects in non-default locations.  (Such locations
> +are listed explicitly in the configuration file for @command{ldconfig},
> +usually @file{/etc/ld.so.conf}, or in files included from there.)
> +
> +@item
> +Do not configure dynamically-loaded NSS service modules, to avoid
> +accidental internal use of the @code{dlopen} facility.  The @code{files}
> +and @code{dns} modules are built in and do not rely on @code{dlopen}.
> +
> +@item
> +Do not truncate and overwrite files containing programs and shared
> +objects in place, while they are used.  Instead, write the new version
> +to a different path and use @code{rename} to replace the
> +already-installed version.
> +
> +@item
> +Be aware that during an update procedure that involves multiple object
> +files (shared objects and main programs), concurrently starting
> +processes may observe an inconsistent combination of object files (some
> +already updated, some still at the previous version).
> +@end itemize
> +
>
>  @c FIXME these are undocumented:
>  @c dladdr
>
> base-commit: 143ef68b2aded7c794956beddad495af8c7d3251
>


-- 
Joe Simmons-Talbott



More information about the Libc-alpha mailing list