[Secure-testing-team] Bug#698064: aranym: crashes from guest userspace when NatFeat is queried

Thorsten Glaser tg at mirbsd.de
Sun Jan 13 18:52:34 UTC 2013


Package: aranym
Version: 0.9.14-2
Severity: grave
Tags: security
Justification: user security hole

When running the program whose source code follows below
the report, compiled with the following command:
	gcc -Os -fno-asynchronous-unwind-tables \
	    -fno-stack-protector -static -o nfimvirt \
	    nfimvirt.c nfimvrth.S

Inside a Debian/m68k guest on ARAnyM running on Debian sid,
the guest crashes the virtualisation:

Gotcha! Illegal memory access. Atari PC = $80000468
If the Full History was enabled you would see the last 20 instructions here.

The program is intended to use NatFeat, as per the specs,
to figure out whether it runs under emulation or not. The
severity stems from this virtualisation escape: an error
or SIGILL or SIGBUS would be an acceptable failure mode,
but the guest must not DoS the emulation (this would make
offering Debian Porterboxen impossible, for one).

This is not exactly a new issue, I think Wouter reported
similar findings in the imvirt wishlist bugreport.

#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.11.1).
# To extract the files from this archive, save it to some FILE, remove
# everything before the `#!/bin/sh' line above, then type `sh FILE'.
#
lock_dir=_sh29846
# Made on 2013-01-13 18:48 UTC by <root at ara4.mirbsd.org>.
# Source directory was `/root'.
#
# Existing files will *not* be overwritten, unless `-c' is specified.
#
# This shar contains:
# length mode       name
# ------ ---------- ------------------------------------------
#   2779 -rw-r--r-- nfimvirt.c
#    447 -rw-r--r-- nfimvrth.S
#
MD5SUM=${MD5SUM-md5sum}
f=`${MD5SUM} --version | egrep '^md5sum .*(core|text)utils'`
test -n "${f}" && md5check=true || md5check=false
${md5check} || \
  echo 'Note: not verifying md5sums.  Consider installing GNU coreutils.'
if test "X$1" = "X-c"
then keep_file=''
else keep_file=true
fi
echo=echo
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=
locale_dir=
set_echo=false

for dir in $PATH
do
  if test -f $dir/gettext \
     && ($dir/gettext --version >/dev/null 2>&1)
  then
    case `$dir/gettext --version 2>&1 | sed 1q` in
      *GNU*) gettext_dir=$dir
      set_echo=true
      break ;;
    esac
  fi
done

if ${set_echo}
then
  set_echo=false
  for dir in $PATH
  do
    if test -f $dir/shar \
       && ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
    then
      locale_dir=`$dir/shar --print-text-domain-dir`
      set_echo=true
      break
    fi
  done

  if ${set_echo}
  then
    TEXTDOMAINDIR=$locale_dir
    export TEXTDOMAINDIR
    TEXTDOMAIN=sharutils
    export TEXTDOMAIN
    echo="$gettext_dir/gettext -s"
  fi
fi
IFS="$save_IFS"
if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null
then if (echo -n test; echo 1,2,3) | grep n >/dev/null
     then shar_n= shar_c='
'
     else shar_n=-n shar_c= ; fi
else shar_n= shar_c='\c' ; fi
f=shar-touch.$$
st1=200112312359.59
st2=123123592001.59
st2tr=123123592001.5 # old SysV 14-char limit
st3=1231235901

if touch -am -t ${st1} ${f} >/dev/null 2>&1 && \
   test ! -f ${st1} && test -f ${f}; then
  shar_touch='touch -am -t $1$2$3$4$5$6.$7 "$8"'

elif touch -am ${st2} ${f} >/dev/null 2>&1 && \
   test ! -f ${st2} && test ! -f ${st2tr} && test -f ${f}; then
  shar_touch='touch -am $3$4$5$6$1$2.$7 "$8"'

elif touch -am ${st3} ${f} >/dev/null 2>&1 && \
   test ! -f ${st3} && test -f ${f}; then
  shar_touch='touch -am $3$4$5$6$2 "$8"'

else
  shar_touch=:
  echo
  ${echo} 'WARNING: not restoring timestamps.  Consider getting and
installing GNU `touch'\'', distributed in GNU coreutils...'
  echo
fi
rm -f ${st1} ${st2} ${st2tr} ${st3} ${f}
#
if test ! -d ${lock_dir} ; then :
else ${echo} "lock directory ${lock_dir} exists"
     exit 1
fi
if mkdir ${lock_dir}
then ${echo} "x - created lock directory ${lock_dir}."
else ${echo} "x - failed to create lock directory ${lock_dir}."
     exit 1
fi
# ============= nfimvirt.c ==============
if test -n "${keep_file}" && test -f 'nfimvirt.c'
then
${echo} "x - SKIPPING nfimvirt.c (file already exists)"
else
${echo} "x - extracting nfimvirt.c (text)"
  sed 's/^X//' << 'SHAR_EOF' > 'nfimvirt.c' &&
#include <sys/types.h>
#include <sys/mman.h>
#include <err.h>
#include <signal.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
X
#ifndef __GNUC__
#error This file makes use of GNU C extensions.
#endif
X
extern long nf_get_id_asm(const char *feature_name)
X	asm("nf_get_id_asm")
X	__attribute__((__cdecl__, __regparm__(0)));
extern long nf_call_asm(unsigned long feature_id, ...)
X	asm("nf_call_asm")
X	__attribute__((__cdecl__, __regparm__(0)));
X
volatile sig_atomic_t got_sigill;
X
void sigill_handler(int sigraised);
long nf_get_id(const char *feature_name);
X
#define nf_call2(id, subid, ...) __extension__({			\
X	long nf_call2_res;						\
X	unsigned long nf_call2_fid;					\
X									\
X	if (got_sigill)							\
X		errx(2, "nf_call2: previous unhandled SIGILL");		\
X	nf_call2_fid = (unsigned long)(id) | (unsigned long)(subid);	\
X	nf_call2_res = nf_call_asm(nf_call2_fid, ## __VA_ARGS__);	\
X	if (got_sigill)							\
X		errx(2, "nf_call2: SIGILL for %08lX", nf_call2_fid);	\
X	(nf_call2_res);							\
})
X
#ifdef __KLIBC__
void err(int, const char *, ...)
X	__attribute__((__noreturn__, __format__(__printf__, 2, 3)));
void errx(int, const char *, ...)
X	__attribute__((__noreturn__, __format__(__printf__, 2, 3)));
#endif
X
int
main(void)
{
X	long NF_NAME, NF_VERSION, sres;
X	char *buf;
X	unsigned long bufsz = 0, ures;
X	int pgsz;
X
X	if ((pgsz = getpagesize()) <= 0 || pgsz > 0x10000000)
X		err(2, "getpagesize() returned %d", pgsz);
X	while (bufsz < 4096)
X		bufsz += pgsz;
X	if ((buf = mmap(NULL, (size_t)bufsz, PROT_READ | PROT_WRITE,
X	    MAP_ANON | MAP_PRIVATE, 0, (off_t)0)) == MAP_FAILED)
X		err(2, "mmap() %lu bytes failed", bufsz);
X	if (mlock(buf, bufsz))
X		err(2, "mlock() failed");
X
X	got_sigill = 0;
X	if (signal(SIGILL, sigill_handler) == SIG_ERR)
X		err(2, "cannot install SIGILL handler");
X
X	if ((NF_NAME = nf_get_id("NF_NAME")) == -1L) {
X		printf("Physical (no NatFeat)\n");
X		return (0);
X	}
X	if ((NF_VERSION = nf_get_id("NF_VERSION")) == -1L) {
X		printf("Ambiguous (broken NatFeat)\n");
X		return (1);
X	}
X
X	sres = nf_call2(NF_VERSION, 0);
X	ures = nf_call2(NF_NAME, 0, buf, bufsz);
X	buf[bufsz - 1] = 0;
X	printf("NatFeat v%d.%d (Emulator%s: %s)", (int)((sres >> 16) & 0xFFFF),
X	    (int)(sres & 0xFFFF), ures >= bufsz ? " (truncated)" : "", buf);
X	ures = nf_call2(NF_NAME, 1, buf, bufsz);
X	buf[bufsz - 1] = 0;
X	printf(" on%s: %s\n", ures >= bufsz ? " (truncated)" : "", buf);
X
X	/* munlock, munmap, signal */
X
X	return (0);
}
X
long
nf_get_id(const char *feature_name)
{
X	long res;
X
X	if (got_sigill)
X		errx(2, "nf_get_id: previous unhandled SIGILL");
X	res = nf_get_id_asm(feature_name);
X	if (got_sigill) {
X		got_sigill = 0;
X		return (-1L);
X	}
X	return (res & 0xFFF00000);
}
X
void
sigill_handler(int sigraised __attribute__((__unused__)))
{
X	got_sigill = 1;
}
SHAR_EOF
  (set 20 13 01 13 18 41 08 'nfimvirt.c'
   eval "${shar_touch}") && \
  chmod 0644 'nfimvirt.c'
if test $? -ne 0
then ${echo} "restore of nfimvirt.c failed"
fi
  if ${md5check}
  then (
       ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'nfimvirt.c': 'MD5 check failed'
       ) << \SHAR_EOF
4715c8dcdd33d3297720a5e0617501ba  nfimvirt.c
SHAR_EOF
  else
test `LC_ALL=C wc -c < 'nfimvirt.c'` -ne 2779 && \
  ${echo} "restoration warning:  size of 'nfimvirt.c' is not 2779"
  fi
fi
# ============= nfimvrth.S ==============
if test -n "${keep_file}" && test -f 'nfimvrth.S'
then
${echo} "x - SKIPPING nfimvrth.S (file already exists)"
else
${echo} "x - extracting nfimvrth.S (text)"
  sed 's/^X//' << 'SHAR_EOF' > 'nfimvrth.S' &&
X	.text
X
X	.p2align 2
X	.globl	nf_get_id_asm
#ifdef __ELF__
X	.type	nf_get_id_asm, at function
#endif
nf_get_id_asm:
X	.byte	0x73		/* NatFeat */
X	.byte	0x00		/* nf_get_id */
X	rts
#ifdef __ELF__
X	.size	nf_get_id_asm, . - nf_get_id_asm
#endif
X
X	.p2align 2
X	.globl	nf_call_asm
#ifdef __ELF__
X	.type	nf_call_asm, at function
#endif
nf_call_asm:
X	.byte	0x73		/* NatFeat */
X	.byte	0x01		/* nf_call */
X	rts
#ifdef __ELF__
X	.size	nf_call_asm, . - nf_call_asm
#endif
SHAR_EOF
  (set 20 13 01 13 18 08 36 'nfimvrth.S'
   eval "${shar_touch}") && \
  chmod 0644 'nfimvrth.S'
if test $? -ne 0
then ${echo} "restore of nfimvrth.S failed"
fi
  if ${md5check}
  then (
       ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'nfimvrth.S': 'MD5 check failed'
       ) << \SHAR_EOF
c0e9906c254931d54f4716dcb42271c9  nfimvrth.S
SHAR_EOF
  else
test `LC_ALL=C wc -c < 'nfimvrth.S'` -ne 447 && \
  ${echo} "restoration warning:  size of 'nfimvrth.S' is not 447"
  fi
fi
if rm -fr ${lock_dir}
then ${echo} "x - removed lock directory ${lock_dir}."
else ${echo} "x - failed to remove lock directory ${lock_dir}."
     exit 1
fi
exit 0


-- System Information:
Debian Release: 7.0
  APT prefers unstable
  APT policy: (500, 'unstable'), (500, 'testing')
Architecture: amd64 (x86_64)

Kernel: Linux 2.6.32-5-xen-amd64 (SMP w/1 CPU core)
Locale: LANG=C, LC_CTYPE=en_GB.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/mksh

Versions of packages aranym depends on:
ii  libc6            2.13-38
ii  libgcc1          1:4.7.2-4
ii  libgmp10         2:5.0.5+dfsg-2
ii  libmpfr4         3.1.0-5
ii  libsdl-image1.2  1.2.12-2
ii  libsdl1.2debian  1.2.15-5
ii  libstdc++6       4.7.2-4
ii  libusb-1.0-0     2:1.0.12-2
ii  libx11-6         2:1.5.0-1
ii  zlib1g           1:1.2.7.dfsg-13

Versions of packages aranym recommends:
ii  bridge-utils   1.5-6
ii  uml-utilities  20070815-1.3

aranym suggests no packages.

-- no debconf information



More information about the Secure-testing-team mailing list