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