[comp.windows.x] Answers Re: SunOS Shared Libraries

gingell%opus@Sun.COM (Rob Gingell) (01/13/90)

There have been a number of messages posted about various problems/issues in
using shared libraries under SunOS -- specifically problems relating in how
libraries are located and used at execution time.  These often manifest 
themselves as a message like:

	ld.so: libfoo.so.n not found

Here's some information about how it works that may help people use these
facilities a little better.  

setxid programs and dynamic linking.
	The dynamic linker will refuse to use shared objects from directories
	that it does not "trust" when running a set-user or set-group id 
	program.  In 4.0[.x], the implementation simply ignores the library
	search path environment variable when runing a setxid program.

	The reason for this is that it would be trivial to usurp the system
	by writing a C library that included a "getuid()" implementation that
	forked a shell, setting the path variable to pick up this C library,
	and then running "su".

	The link editor "trusts" /usr/lib, /usr/5lib, and /usr/local/lib.

	In 4.0[.x], there is no way to tell the linker that other directories
	are "trustworthy."  So, if you have a library that you want to have
	dynamically linked in a setxid program, it needs to be in one of these
	directories (rather, a name for the library needs to exist in one of
	these directories.)  The bottom line for you is to simply put a 
	symbolic link to the libraries needed by setxid programs in these
	directories.

	Just so you know, 4.1 changes this slightly.  The 4.1 dynamic linker
	does not just ignore the LD_LIBRARY_PATH variable, but instead will
	only use the elements it finds in it that it will trust.  This allows
	one, for instance, to change the search order among trusted libraries
	if for some reason you thought that'd be useful.

	There is also a change to what ld.so considers trusted, in the
	specific case of setxid programs.  While not a general solution
	to the matter, this would deal effectively with the specific
	problems discussed in this group.

		-L options specified during the ld'ing of a dynamically
		linked executable are retained in the executable.  This
		way you can build into a program that its shared libraries
		live in a non-default directory without requiring everyone
		to use the LD_LIBRARY_PATH environment variable.  (This
		is true even in 4.0[.x].)

		What's different in 4.1 is that if a program is ld'ed with
		-L options, and then run as a setxid program, then the
		dynamic linker will "trust" all the "-L" directories.

		So, if you build something like xterm using "-L" rather than
		LD_LIBRARY_PATH to specify X client shared libraries, and
		then ran it as a setxid program, under 4.1 the directory(s)
		specified in a "-L" option would be considered trusted for
		the exectution of "xterm".  (They would not be considered
		trusted for other programs that were not built with that
		directory specified with "-L", such as "su" -- the "trust"
		extends only as far as the program built with "-L".)

	Until 4.1, or if you don't want to depend on 4.1, you should put
	links in /usr/local/lib or /usr/lib to X libraries required by
	setxid executables.

"ldconfig."
	When a dynamically linked program is executed, the libraries it
	requires are located using an algorithm that involves searching a
	sequence of directories for the "best available" version of the
	library.  The ability to adjust the search rules on a per-process
	basis means that this evaluation must be evaluated for every process.
	Although the search is usually fast enough to not be noticable in
	the course of interactive use -- the cumulative time over all
	executions can be non-trivial.  So the dynamic linker uses a
	"cache" that has precomputed where the "best" of a given library
	is in a given directory.

	The cache is built up of elements that are constructed on a
	per-directory basis.  Each element contains information that tells what
	the best revision of each interface is contained in which file for all
	libraries contained in that directory.  For instance, if /usr/lib
	contained:

		libc.so.1.3
		libc.so.1.4
		libc.so.2.1

	and /usr/local/lib contained

		libfoo.so.5.3
		libfoo.so.5.15

	then a cache with elements for /usr/lib and /usr/local/lib would
	contain:

		/usr/lib:
			libc.1 -> libc.so.1.4
			libc.2 -> libc.so.2.1

		/usr/local/lib
			libfoo.5 -> libfoo.so.5.15

	If you change a directory associated with an element without causing
	the element to be re-evaluated, the dynamic linker will fail to find
	any updates you've made.  So, if you added

		/usr/lib/libc.so.2.2

	without recomputing the element for /usr/lib, no program would ever
	find the newly revised version of interface 2 of the C library.  [As it
	turns out, if you add an entirely new interface, such as a version
	"3.x" of the C library, such that a search through caches yields *no*
	candidate, the link editor will automatically invalidate the cache and
	recompute it for the current process -- however, new revisions don't
	cause this behavior simply because the cache does retrieve the old
	revision.]

	"ldconfig" is the program that recomputes the elements -- by default
	it does /usr/lib, /usr/local/lib, and /usr/5lib.  You can ask that
	other directories be included by having them as command line
	arguments to "ldconfig."

	If you don't do this for a directory, the only side effect is that the
	dynamic linker will have to read the directory to find the best
	versions of whatever libraries it holds.  If you don't do this for a
	directory that *is* in the cache already, but is one that you've
	updated with a new revision, then the new revision will simply not be
	visible.  In the case where you don't cache a directory, processes
	wishing to use that directory in finding shared libraries will simply
	run a little slower.  In particular, whether "ldconfig" builds a cache
	element for a directory or not has no impact on whether or not that
	directory is considered "trusted."

-assert pure-text
	In at least one of the messages I saw, there was a discussion that
	indicated that the use of this option was producing some "ld" 
	diagnostics -- this was mentioned in the context of whether or
	not the library could be found at execution time.  I may have mis-
	understood that specific reference but the only thing the "-assert
	pure-text" ld option does is have "ld" tell you whether or not it
	had to defer some text-segment relocation.  If it does, the penalty
	is that the dynamic linker will have to resolve that relocation at
	execution-time, leading to increased swap usage for the modified
	pages and less sharing than perhaps you expected.   The diagnostic
	it produces isn't particularly helpful, this is improved in 4.1
	to provide information that's more immediately useful to someone
	building the library.  However, it's completely separate from the
	process by which libraries are found at execution time.

Do shared libraries buy you anything?
	It depends upon what you value.  If a library is used by multiple
	processes (i.e., sharing is really going on) then experience with
	the libraries Sun supplies says that "yes, you save a lot of both
	disk space and memory occupancy, and indirectly save on channel
	bandwidth due to decreased I/O requirements."  If on the other
	hand, a shared library is used only serially (i.e. at any time only
	one process exists that uses the library), then it is conceivable
	(and probable) that you've actually increased the load on the
	primary memory resource because you've got the *whole* library
	rather than just what you need -- and this probably spans more
	address space and therefore more pages than it would have had you
	extracted just what you need from an archive.

	However, Sun ships a shared library that isn't shared (in parallel,
	that is.)  It's the libkvm library that is bound with programs that
	rummage through kernel memory like "ps", "netstat", etc.  It's 
	highly unlikely on a workstation that you'll actually have two
	processes doing this simultaneously.  However the reason libkvm is
	supplied as a .so and used that way in the system has to do with
	"dynamic linking" -- not the application of dynamic linking that
	creates the effect of library sharing.

	We want to dynamically link libkvm to isolate those applications
	that interrogate kernel memory from the access methods used to do
	that interrogation.  At the time the dynamic linking facilities
	were in development, we were also developing the memory management
	facilities and it was somewhat painful to have to re-cut all the
	"kernel virtual memory" programs as a result of some experiment or
	change we conducted with the memory management code.  What we value
	with libkvm is not the sharing, but the flexibility provided by
	dynamic linking.  You may also value this in some circumstances,
	where you might not value library sharing.  So, at least with the
	dynamically linked shared library implementations in SunOS and
	SVR4, the issue of whether "to .so or not to .so" is a little
	more subtle than simply library sharing.

casey@gauss.llnl.gov (Casey Leedom) (01/19/90)

| From: gingell%opus@Sun.COM (Rob Gingell)
| 
| setxid programs and dynamic linking.
| 
| The dynamic linker will refuse to use shared objects from directories
| that it does not "trust" when running a set-user or set-group id
| program.  In 4.0[.x], the implementation simply ignores the library
| search path environment variable when runing a setxid program.  The link
| editor "trusts" /usr/lib, /usr/5lib, and /usr/local/lib.  In 4.0[.x],
| there is no way to tell the linker that other directories are
| "trustworthy."  So, if you have a library that you want to have
| dynamically linked in a setxid program, it [or a symbolic link pointing
| at it] needs to be in one of these directories.
| 
| [In 4.1] There is also a change to what ld.so considers trusted, in the
| specific case of setxid programs.  What's different in 4.1 is that if a
| program is ld'ed with -L options, and then run as a setxid program, then
| the dynamic linker will "trust" all the "-L" directories.

Rob,
  Thanks for the extremely complete and clear posting on SunOS 4.X
shared library dynamic linking.  I'm sure that it will help people by
keeping them from fruitlessly pursuing incorrect notions of how SunOS 4.X
shared libraries work.  I know that it will definitely save me time, so I
thank you for that!

  However (you knew there had to be a catch to all this praise, didn't
you), I view the current behavior as a bug, and your description of how
4.1 will allow people to work around it as a hack.  The bug that I
perceive is that ld.so doesn't trust the libraries in the directories
covered by ldconfig's cache.

	[[It's also a bug that the semantics that you describe are not
	covered in any SunOS documentation that I know of, but I'm sure
	that your posting will be attached to future SunOS 4.0.X
	distributions and will be integrated into the SunOS 4.1
	documentation.]]

Since you must be root to run ldconfig, having ld.so trust ldconfig's
cache won't introduce any security holes (as long as the fact that they
become trusted is documented).

  I view your description of the SunOS 4.1 -L handling to be a hack
because it suddenly introduces a completely new mechanism to determine
trusted libraries.  Now it may be that this turns out to be The Right
Way, but I'm suspicious of any setup that requires users to deal with
multiple mechanisms to accomplish the same thing.  If nothing else, you're
asking for a lot of user grief and many calls to Sun software support.

  Personally I think Sun should unify its shared library support and
trusted library directories via ldconfig:

    1.	Since it's likely that administrators will probably want to add
	local site dependent directories to those cached by default by
	SunOS.  In order to avoid having to edit two files (/etc/rc.local
	and /var/spool/cron/crontabs/root (which, by the way, *doesn't*
	have an ldconfig entry as advertised in the ldconfig(8) man
	page)), I suggest that ldconfig acquire it's default set of
	directories to cache from a configuration file, say
	/etc/ldconfig.conf.

    2.	Since it's likely that administrators will probably want some
	of those directories to be trusted, but not others, I suggest
	that the ldconfig'uration file contain authentication information.
	A high degree of flexibility would be granted to administrators
	if they could resolve this authentication down to the individual
	library if desired.

Thus:
    /etc/ldconfig:
	# 
	# @(#)ldconfig.conf	1.1 (SMI) 1/18/90
	# 
	# Shared library cache and ``trust'' authentication configuration
	# file.  Lines consist of library or directory paths optionally
	# followed by some number of spaces and/or tabs, followed by
	# authentication information describing the level of ``trust'' for
	# the libraries (or libraries within a directory.)  Lines without
	# explicit authentication information will infer the lowest level of
	# ``trust'' authentication, FOREIGN (see below).
	# 
	# If a directory is listed here, only the libraries contained
	# directly within that directory are inferred.  That is, caching and
	# authentication are NOT inherited by subdirectories of directories
	# listed here.
	# 
	# Authentication lines for individual libraries override
	# authentication lines for the directory the libraries are found in.
	# 
	# Current authentications are:
	# 
	# TRUSTED	Libraries will be trusted for SUID and
	# 		SGID objects.
	# 
	# FOREIGN	Libraries will not be trusted for SUID or SGID objects.
	# 		Such objects when dynamically linked against FOREIGN
	# 		libraries will generate a warning and be run with
	# 		the UID/GID of the process running executing the
	# 		object.  This is the default ``trust'' authentication
	# 		if none is explicitly given.
	# 
	# library [directory]	level of trust
	# 
	/usr/lib		TRUSTED
	/usr/5lib		TRUSTED
	/usr/local/lib		TRUSTED
	/usr/local/lib/X11	TRUSTED
	/usr/new/lib		FOREIGN
	/usr/new/lib/proj	TRUSTED
	/usr/local/lib/libzap.a	FOREIGN

While this seems like a lot, it's my expectation that most of the pieces
are already present to do this: there are plenty of unused bits in
lo_unused to implement a few bits of authentication (say at least four
for future expansion) and ld.so already has to read /etc/ld.so.cache in
any case.  It would probably require relatively trivial changes to
ldconfig and ld.so to implement this.

  Alternatives to TRUSTED/FOREIGN might be "SUID", "SGID", "SUID,SGID",
and "FOREIGN" (== 0) (i.e. a set oriented authentication), etc.

Casey