COMMAND
kernel
SYSTEMS AFFECTED
FreeBSD 3.x, 4.x and 5.x prior to the correction date
PROBLEM
Following is based on a FreeBSD-SA-00:42 Security Advisory by
FreeBSD and it was found by Boris Nikolaus. FreeBSD is
binary-compatible with the Linux operating system through a
loadable kernel module/optional kernel component.
The linux binary-compatability module implements a "shadow"
filesystem hierarchy rooted in /compat/linux, which is overlayed
against the regular filesystem hierarchy so that Linux binaries
"see" files in the shadow hierarchy which can mask the native
files.
Filenames in this shadow hierarchy are treated incorrectly by the
linux kernel module under certain circumstances, and a kernel
stack overflow leading to a system compromise by an unprivileged
user may be possible when very long filenames are used. This is
only possible when the linux kernel module is loaded, or the
equivalent functionality is statically compiled into the kernel.
It is not enabled by default.
This vulnerability was fixed just after the release of FreeBSD
4.1-RELEASE, and 3.5-RELEASE is also vulnerable.
Local users may be able to obtain root privileges on the system
when linux compatability mode is enabled.
SOLUTION
To determine whether the linux compatability module has been
loaded, execute the following command as root and look for a
'linux.ko' entry:
# kldstat
Id Refs Address Size Name
1 7 0xc0100000 270be0 kernel
2 1 0xc0371000 5540 vesa.ko
3 1 0xc0377000 10094 randomdev.ko
4 1 0xc0e17000 4e000 nfs.ko
5 1 0xc0e83000 11000 linux.ko
If present, unload the "linux" module by executing the following
command as root:
# kldunload linux
For safety, remove the /modules/linux.ko file to prevent it being
reloaded accidentally, and add or change the following line in
/etc/rc.conf:
linux_enable="NO" # Linux binary compatibility loaded at startup (or NO).
If the module is not loaded, to determine whether the
functionality has been statically compiled into the kernel, check
the kernel configuration file for the following line:
options COMPAT_LINUX
If present, remove and recompile the kernel as described in
http://www.freebsd.org/handbook/kernelconfig.html and reboot the
system.
Solution is to upgrade your vulnerable FreeBSD system to
3.5-STABLE, 4.1-STABLE or 5.0-CURRENT after the respective
correction dates or apply the patch below and recompile your
kernel:
ftp://ftp.freebsd.org/pub/FreeBSD/CERT/patches/SA-00:42/linux.patch
Patch:
Index: linux_misc.c
===================================================================
RCS file: /home/ncvs/src/sys/i386/linux/linux_misc.c,v
retrieving revision 1.77.2.3
retrieving revision 1.77.2.4
diff -u -r1.77.2.3 -r1.77.2.4
--- linux_misc.c 2000/07/20 05:31:56 1.77.2.3
+++ linux_misc.c 2000/07/30 05:36:11 1.77.2.4
@@ -954,6 +954,8 @@
tv[1].tv_usec = 0;
/* so that utimes can copyin */
tvp = (struct timeval *)stackgap_alloc(&sg, sizeof(tv));
+ if (tvp == NULL)
+ return (ENAMETOOLONG);
if ((error = copyout(tv, tvp, sizeof(tv))))
return error;
bsdutimes.tptr = tvp;
Index: linux_util.c
===================================================================
RCS file: /home/ncvs/src/sys/i386/linux/linux_util.c,v
retrieving revision 1.9.2.1
retrieving revision 1.9.2.2
diff -u -r1.9.2.1 -r1.9.2.2
--- linux_util.c 2000/07/07 01:23:45 1.9.2.1
+++ linux_util.c 2000/07/30 05:36:11 1.9.2.2
@@ -162,7 +162,10 @@
else {
sz = &ptr[len] - buf;
*pbuf = stackgap_alloc(sgp, sz + 1);
- error = copyout(buf, *pbuf, sz);
+ if (*pbuf != NULL)
+ error = copyout(buf, *pbuf, sz);
+ else
+ error = ENAMETOOLONG;
free(buf, M_TEMP);
}
Index: linux_util.h
===================================================================
RCS file: /home/ncvs/src/sys/i386/linux/linux_util.h,v
retrieving revision 1.10
retrieving revision 1.10.2.1
diff -u -r1.10 -r1.10.2.1
--- linux_util.h 1999/12/04 11:10:22 1.10
+++ linux_util.h 2000/07/30 05:36:11 1.10.2.1
@@ -56,29 +56,27 @@
static __inline caddr_t stackgap_init(void);
static __inline void *stackgap_alloc(caddr_t *, size_t);
+#define szsigcode (*(curproc->p_sysent->sv_szsigcode))
+
static __inline caddr_t
stackgap_init()
{
-#define szsigcode (*(curproc->p_sysent->sv_szsigcode))
return (caddr_t)(PS_STRINGS - szsigcode - SPARE_USRSPACE);
}
-
static __inline void *
stackgap_alloc(sgp, sz)
caddr_t *sgp;
size_t sz;
{
- void *p = (void *) *sgp;
- *sgp += ALIGN(sz);
+ void *p = (void *) *sgp;
+
+ sz = ALIGN(sz);
+ if (*sgp + sz > (caddr_t)(PS_STRINGS - szsigcode))
+ return NULL;
+ *sgp += sz;
return p;
}
-
-#ifdef DEBUG_LINUX
-#define DPRINTF(a) printf a;
-#else
-#define DPRINTF(a)
-#endif
extern const char linux_emul_path[];