Bug 127276

Summary: [patch] ldd(1) invokes linux yes
Product: Base System Reporter: kamikaze
Component: binAssignee: Mark Johnston <markj>
Status: Closed FIXED    
Severity: Affects Only Me    
Priority: Normal    
Version: Unspecified   
Hardware: Any   
OS: Any   

Description kamikaze 2008-09-10 18:50:01 UTC
When ldd is used on linux yes it invokes it instead of producing the usual output.

# pkg_info -W /compat/linux/usr/bin/yes
/compat/linux/usr/bin/yes was installed by package linux_base-f8-8_4
# sysctl compat.linux.osrelease
compat.linux.osrelease: 2.6.16

This behaviour breaks pkg_libchk from the sysutils/bsdadminscripts port.

How-To-Repeat: # ldd /compat/linux/usr/bin/yes
Comment 1 John Baldwin freebsd_committer freebsd_triage 2008-09-10 21:10:13 UTC
ldd is not going to work for Linux binaries.  The Linux ldd should be used for 
Linux binaries.

-- 
John Baldwin
Comment 2 kamikaze 2008-09-11 06:01:09 UTC
I don't need it to work, I just need it not to invoke linux binaries. I'm
using ldd in a script and by ldd not returning 0 the script should know that
it hasn't encountered a valid binary. Instead ldd opens a linux binary like
yes and the script spills out ys (yes) or waits for input from stdin
(md5sum). I'm pretty certain ldd is in no way meant to invoke programs.
Comment 3 Rui Paulo freebsd_committer freebsd_triage 2008-09-11 13:10:58 UTC
I chatted briefly with John about this. The way our ldd works is by
setting the environment variable TRACE_LOADED_OBJECTS and after some
dlopen() magic it exec()'s the binary. FreeBSD rtld detects the environmental
variable, prints the list of shared objects and quits.

Linux rtld doesn't work this way, so FreeBSD ldd on a Linux binary will
just run the Linux binary (Linux rtld will ignore the rest).

Also, ldd wil return 1 on static binaries. Your best bet is to use
file(1) to detect FreeBSD binaries. Something like `file $binary | grep
FreeBSD-style` does the trick.

Regards,
-- 
Rui Paulo
Comment 4 John Baldwin freebsd_committer freebsd_triage 2008-09-11 15:38:32 UTC
As Rui indicated, ldd always execs binaries.  It just sets environment 
variables that the FreeBSD runtime linker checks for.  If the runtime linker 
sees them, it will modify it's behavior.  You can achieve the same thing 
using env:

% ldd /bin/ls
/bin/ls:
        libutil.so.7 => /lib/libutil.so.7 (0x2808b000)
        libncurses.so.7 => /lib/libncurses.so.7 (0x28099000)
        libc.so.7 => /lib/libc.so.7 (0x280d8000)
% env LD_TRACE_LOADED_OBJECTS=yes /bin/ls
        libutil.so.7 => /lib/libutil.so.7 (0x2808b000)
        libncurses.so.7 => /lib/libncurses.so.7 (0x28099000)
        libc.so.7 => /lib/libc.so.7 (0x280d8000)
 
All the "ldd" printfs, etc. are actually from the runtime linker, not ldd 
itself.  The Linux runtime linker doesn't modify it's behavior for 
LD_TRACE_LOADED_OBJECTS, so Linux apps just run normally when invoked by ldd.

-- 
John Baldwin
Comment 5 kamikaze 2008-09-11 17:37:23 UTC
Ok, thanks. I have already created a workaround with readelf, but I'd still
consider this a bug in ldd. Shouldn't it check the elf brand if it only
works for a single one?

Regards
Comment 6 Mark Linimon 2008-09-12 00:29:24 UTC
----- Forwarded message from John Baldwin <jhb@freebsd.org> -----

FreeBSD binaries from various releases have been branded in different ways.  I 
would consider it more of a user error to run ldd on a Linux binary. :)  You 
could maybe add a "IMPLEMENTATION NOTES" section to the manpage that explains 
how it works and why it will execute any binary using a different runtime 
linker.

-- 
John Baldwin

----- End forwarded message -----
Comment 7 Mark Linimon freebsd_committer freebsd_triage 2008-09-16 03:55:49 UTC
State Changed
From-To: open->suspended

As noted in the feedback trail, the fix is not as obvious as it first 
might appear.  Mark this as 'suspended' for now to note that although 
we understand the problem, it is going to need more research.
Comment 8 kamikaze 2009-07-03 08:22:37 UTC
How about documenting this behaviour in the manual page and
closing this bug? I'm willing to write a patch if someone
thinks this would be an acceptable solution.
Comment 9 Shuichi KITAGUCHI 2010-05-04 04:21:45 UTC
----Next_Part(Tue_May_04_12_21_45_2010_795)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Hello,

I investigate this problem today and found that Linux's ld.so
accepts "LD_TRACE_LOADED_OBJECTS".

% env LD_TRACE_LOADED_OBJECTS=yes /compat/linux/bin/ls
        librt.so.1 => /lib/librt.so.1 (0x28087000)
        libselinux.so.1 => /lib/libselinux.so.1 (0x28092000)
        libcap.so.2 => /lib/libcap.so.2 (0x280af000)
        libacl.so.1 => /lib/libacl.so.1 (0x280b4000)
        libc.so.6 => /lib/libc.so.6 (0x280bc000)
        libpthread.so.0 => /lib/libpthread.so.0 (0x28234000)
        /lib/ld-linux.so.2 (0x28063000)
        libdl.so.2 => /lib/libdl.so.2 (0x2824e000)
        libattr.so.1 => /lib/libattr.so.1 (0x28254000)

I think environment variable "LD_32_TRACE_LOADED_OBJECTS" is
FreeBSD specific, and "LD_TRACE_LOADED_OBJECTS" should be passed
to other binaries.

Attached patch is reasonable?


--
Shuichi KITAGUCHI // kit@ysnb.net / ki@hh.iij4u.or.jp

----Next_Part(Tue_May_04_12_21_45_2010_795)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="ldd.c.patch"

--- ldd.c.old	2009-09-19 21:45:17.000000000 +0900
+++ ldd.c	2010-05-04 11:48:42.000000000 +0900
@@ -65,7 +65,7 @@
 #endif
 
 static int	is_executable(const char *fname, int fd, int *is_shlib,
-		    int *type);
+			      int *osabi, int *type);
 static void	usage(void);
 
 #define	TYPE_UNKNOWN	0
@@ -177,14 +177,14 @@
 
 	rval = 0;
 	for (; argc > 0; argc--, argv++) {
-		int fd, status, is_shlib, rv, type;
+		int fd, status, is_shlib, rv, type, osabi;
 
 		if ((fd = open(*argv, O_RDONLY, 0)) < 0) {
 			warn("%s", *argv);
 			rval |= 1;
 			continue;
 		}
-		rv = is_executable(*argv, fd, &is_shlib, &type);
+		rv = is_executable(*argv, fd, &is_shlib, &osabi, &type);
 		close(fd);
 		if (rv == 0) {
 			rval |= 1;
@@ -197,6 +197,8 @@
 			break;
 #if __ELF_WORD_SIZE > 32 && defined(ELF32_SUPPORTED)
 		case TYPE_ELF32:
+			if (osabi != ELFOSABI_FREEBSD)
+				break;
 			rval |= execldd32(*argv, fmt1, fmt2, aflag, vflag);
 			continue;
 #endif
@@ -267,7 +269,7 @@
 }
 
 static int
-is_executable(const char *fname, int fd, int *is_shlib, int *type)
+is_executable(const char *fname, int fd, int *is_shlib, int *osabi, int *type)
 {
 	union {
 		struct exec aout;
@@ -300,6 +302,8 @@
 		return (1);
 	}
 
+	*osabi = hdr.elf.e_ident[EI_OSABI];
+
 #if __ELF_WORD_SIZE > 32 && defined(ELF32_SUPPORTED)
 	if ((size_t)n >= sizeof(hdr.elf32) && IS_ELF(hdr.elf32) &&
 	    hdr.elf32.e_ident[EI_CLASS] == ELFCLASS32) {

----Next_Part(Tue_May_04_12_21_45_2010_795)----
Comment 10 Alexander Best freebsd_committer freebsd_triage 2010-08-23 23:03:43 UTC
State Changed
From-To: suspended->open

Back to open, because a patch has been submitted. 


Comment 11 Alexander Best freebsd_committer freebsd_triage 2010-08-23 23:03:43 UTC
Responsible Changed
From-To: freebsd-amd64->freebsd-bugs

This issue doesn't seem to be amd64 specific.
Comment 12 kamikaze 2011-01-13 10:44:06 UTC
I tested the patch, prior to the test:

> ldd /compat/linux/usr/bin/yes
y
y
y
y
y
y
...

After I applied the patch:
> ldd /compat/linux/usr/bin/yes
/compat/linux/usr/bin/yes:
	libc.so.6 => /lib/libc.so.6 (0x28075000)
	/lib/ld-linux.so.2 (0x2804f000)

Apparently it returns libraries from the /compat/linux perspective.

> ldd /compat/linux/lib/ld-linux.so.2
ldd: /compat/linux/lib/ld-linux.so.2: not a FreeBSD ELF shared object

The behaviour caused by the patch is comprehensible and a vast
improvement over the old behaviour in my opinion, so I'm all for
it.

At some point the manual page of ldd should be updated to describe
the new behaviour. If the patch is committed, I volunteer to make
the changes.

Because the patch was mangled by Gnats, I resubmit it here. I did
not make any changes apart from the header to trigger the usual
patch detection magic.

Regards

diff -u ldd.c.old ldd.c
--- ldd.c.old	2009-09-19 21:45:17.000000000 +0900
+++ ldd.c	2010-05-04 11:48:42.000000000 +0900
@@ -65,7 +65,7 @@
 #endif
 
 static int	is_executable(const char *fname, int fd, int *is_shlib,
-		    int *type);
+			      int *osabi, int *type);
 static void	usage(void);
 
 #define	TYPE_UNKNOWN	0
@@ -177,14 +177,14 @@
 
 	rval = 0;
 	for (; argc > 0; argc--, argv++) {
-		int fd, status, is_shlib, rv, type;
+		int fd, status, is_shlib, rv, type, osabi;
 
 		if ((fd = open(*argv, O_RDONLY, 0)) < 0) {
 			warn("%s", *argv);
 			rval |= 1;
 			continue;
 		}
-		rv = is_executable(*argv, fd, &is_shlib, &type);
+		rv = is_executable(*argv, fd, &is_shlib, &osabi, &type);
 		close(fd);
 		if (rv == 0) {
 			rval |= 1;
@@ -197,6 +197,8 @@
 			break;
 #if __ELF_WORD_SIZE > 32 && defined(ELF32_SUPPORTED)
 		case TYPE_ELF32:
+			if (osabi != ELFOSABI_FREEBSD)
+				break;
 			rval |= execldd32(*argv, fmt1, fmt2, aflag, vflag);
 			continue;
 #endif
@@ -267,7 +269,7 @@
 }
 
 static int
-is_executable(const char *fname, int fd, int *is_shlib, int *type)
+is_executable(const char *fname, int fd, int *is_shlib, int *osabi, int *type)
 {
 	union {
 		struct exec aout;
@@ -300,6 +302,8 @@
 		return (1);
 	}
 
+	*osabi = hdr.elf.e_ident[EI_OSABI];
+
 #if __ELF_WORD_SIZE > 32 && defined(ELF32_SUPPORTED)
 	if ((size_t)n >= sizeof(hdr.elf32) && IS_ELF(hdr.elf32) &&
 	    hdr.elf32.e_ident[EI_CLASS] == ELFCLASS32) {
Comment 13 dfilter service freebsd_committer freebsd_triage 2013-08-07 01:28:30 UTC
Author: markj
Date: Wed Aug  7 00:28:17 2013
New Revision: 254018
URL: http://svnweb.freebsd.org/changeset/base/254018

Log:
  Pass variables prefixed with both LD_ and LD_32_ to the run-time linker.
  This prevents unintentional execution of programs when running ldd(1) on
  32-bit Linux binaries.
  
  PR:		175339, 127276
  Suggested by:	kib, rstone
  Reviewed by:	kib
  MFC after:	2 weeks

Modified:
  head/usr.bin/ldd/ldd.c

Modified: head/usr.bin/ldd/ldd.c
==============================================================================
--- head/usr.bin/ldd/ldd.c	Wed Aug  7 00:20:30 2013	(r254017)
+++ head/usr.bin/ldd/ldd.c	Wed Aug  7 00:28:17 2013	(r254018)
@@ -49,12 +49,6 @@ __FBSDID("$FreeBSD$");
 
 #include "extern.h"
 
-#ifdef COMPAT_32BIT
-#define	LD_	"LD_32_"
-#else
-#define	LD_	"LD_"
-#endif
-
 /*
  * 32-bit ELF data structures can only be used if the system header[s] declare
  * them.  There is no official macro for determining whether they are declared,
@@ -64,6 +58,16 @@ __FBSDID("$FreeBSD$");
 #define	ELF32_SUPPORTED
 #endif
 
+#define	LDD_SETENV(name, value, overwrite) do {		\
+	setenv("LD_" name, value, overwrite);		\
+	setenv("LD_32_" name, value, overwrite);	\
+} while (0)
+
+#define	LDD_UNSETENV(name) do {		\
+	unsetenv("LD_" name);		\
+	unsetenv("LD_32_" name);	\
+} while (0)
+
 static int	is_executable(const char *fname, int fd, int *is_shlib,
 		    int *type);
 static void	usage(void);
@@ -82,7 +86,7 @@ execldd32(char *file, char *fmt1, char *
 	char *argv[8];
 	int i, rval, status;
 
-	unsetenv(LD_ "TRACE_LOADED_OBJECTS");
+	LDD_UNSETENV("TRACE_LOADED_OBJECTS");
 	rval = 0;
 	i = 0;
 	argv[i++] = strdup(_PATH_LDD32);
@@ -121,7 +125,7 @@ execldd32(char *file, char *fmt1, char *
 	}
 	while (i--)
 		free(argv[i]);
-	setenv(LD_ "TRACE_LOADED_OBJECTS", "yes", 1);
+	LDD_SETENV("TRACE_LOADED_OBJECTS", "yes", 1);
 	return (rval);
 }
 #endif
@@ -210,15 +214,15 @@ main(int argc, char *argv[])
 		}
 
 		/* ld.so magic */
-		setenv(LD_ "TRACE_LOADED_OBJECTS", "yes", 1);
+		LDD_SETENV("TRACE_LOADED_OBJECTS", "yes", 1);
 		if (fmt1 != NULL)
-			setenv(LD_ "TRACE_LOADED_OBJECTS_FMT1", fmt1, 1);
+			LDD_SETENV("TRACE_LOADED_OBJECTS_FMT1", fmt1, 1);
 		if (fmt2 != NULL)
-			setenv(LD_ "TRACE_LOADED_OBJECTS_FMT2", fmt2, 1);
+			LDD_SETENV("TRACE_LOADED_OBJECTS_FMT2", fmt2, 1);
 
-		setenv(LD_ "TRACE_LOADED_OBJECTS_PROGNAME", *argv, 1);
+		LDD_SETENV("TRACE_LOADED_OBJECTS_PROGNAME", *argv, 1);
 		if (aflag)
-			setenv(LD_ "TRACE_LOADED_OBJECTS_ALL", "1", 1);
+			LDD_SETENV("TRACE_LOADED_OBJECTS_ALL", "1", 1);
 		else if (fmt1 == NULL && fmt2 == NULL)
 			/* Default formats */
 			printf("%s:\n", *argv);
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
Comment 14 Mark Johnston freebsd_committer freebsd_triage 2013-08-07 01:39:53 UTC
State Changed
From-To: open->patched

This has been fixed with r254018 by passing both LD_TRACE_LOADED_OBJECTS 
and LD_32_TRACE_LOADED_OBJECTS to the runtime linker. 


Comment 15 Mark Johnston freebsd_committer freebsd_triage 2013-08-07 01:39:53 UTC
Responsible Changed
From-To: freebsd-bugs->markj

This has been fixed with r254018 by passing both LD_TRACE_LOADED_OBJECTS 
and LD_32_TRACE_LOADED_OBJECTS to the runtime linker.
Comment 16 Mark Johnston freebsd_committer freebsd_triage 2013-09-04 00:22:53 UTC
State Changed
From-To: patched->closed

A fix has been merged to stable/8 and stable/9.
Comment 17 commit-hook freebsd_committer freebsd_triage 2022-10-06 15:50:51 UTC
A commit in branch main references this bug:

URL: https://cgit.FreeBSD.org/src/commit/?id=9cabef3d146e9a844813b6bc8952d6cf2e9d45e5

commit 9cabef3d146e9a844813b6bc8952d6cf2e9d45e5
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2022-09-21 13:55:44 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2022-10-06 15:50:26 +0000

    ldd: use direct exec mode unconditionally

    Trying to exec malformed or unusual binary, for instance, a non-FreeBSD
    ABI, or using a non-standard interpreter, might give unexpected outcome.

    Reported by:    The UK's National Cyber Security Centre (NCSC)
    Reviewed by:    emaste, markj, philip
    Discussed with: jhb
    Sponsored by:   The FreeBSD Foundation
    admbug: 991
    PR:     127276, 175339, 231926
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D36650

 usr.bin/ldd/ldd.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)
Comment 18 commit-hook freebsd_committer freebsd_triage 2022-10-13 04:47:14 UTC
A commit in branch stable/13 references this bug:

URL: https://cgit.FreeBSD.org/src/commit/?id=a763331899b4bb5502382ce1d5dfc8a35d857e27

commit a763331899b4bb5502382ce1d5dfc8a35d857e27
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2022-09-21 13:55:44 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2022-10-13 04:46:01 +0000

    ldd: use direct exec mode unconditionally

    PR:     127276, 175339, 231926

    (cherry picked from commit 9cabef3d146e9a844813b6bc8952d6cf2e9d45e5)

 usr.bin/ldd/ldd.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)