On Wed, Jun 08, 2016 at 09:30:35AM +0300, Riku Voipio wrote: > On Fri, May 27, 2016 at 03:51:59PM +0100, Peter Maydell wrote: > > The siginfo_t struct includes a union. The correct way to identify > > which fields of the union are relevant is complicated, because we > > have to use a combination of the si_code and si_signo to figure out > > which of the union's members are valid. (Within the host kernel it > > is always possible to tell, but the kernel carefully avoids giving > > userspace the high 16 bits of si_code, so we don't have the > > information to do this the easy way...) We therefore make our best > > guess, bearing in mind that a guest can spoof most of the si_codes > > via rt_sigqueueinfo() if it likes. Once we have made our guess, we > > record it in the top 16 bits of the si_code, so that tswap_siginfo() > > later can use it. tswap_siginfo() then strips these top bits out > > before writing si_code to the guest (sign-extending the lower bits). > > > > This fixes a bug where fields were sometimes wrong; in particular > > the LTP kill10 test went into an infinite loop because its signal > > handler got a si_pid value of 0 rather than the pid of the sending > > process. > > > > As part of this change, we switch to using __put_user() in the > > tswap_siginfo code which writes out the byteswapped values to > > the target memory, in case the target memory pointer is not > > sufficiently aligned for the host CPU's requirements. > > At least on Debian jessie, this blows up a selection of architectures: > > CC microblazeel-linux-user/linux-user/signal.o > CC cris-linux-user/linux-user/signal.o > CC microblaze-linux-user/linux-user/signal.o > CC sparc-linux-user/linux-user/signal.o > CC sparc64-linux-user/linux-user/signal.o > CC x86_64-linux-user/linux-user/signal.o > CC unicore32-linux-user/linux-user/signal.o > CC sparc32plus-linux-user/linux-user/signal.o > /home/voipio/linaro/qemu/linux-user/signal.c: In function > ‘host_to_target_siginfo’: > /home/voipio/linaro/qemu/linux-user/signal.c:387:10: error: > ‘tgt_tmp._sifields._sigchld._stime’ may be used uninitialized in this > function [-Werror=maybe-uninitialized] > __put_user(info->_sifields._sigchld._stime,
On the second look, this compile fail only happens if the next patch that introduces tgt_tmp variable is also applied. Dropping that patch instead. > /home/voipio/linaro/qemu/linux-user/signal.c:403:22: note: > ‘tgt_tmp._sifields._sigchld._stime’ was declared here > target_siginfo_t tgt_tmp; > ^ > /home/voipio/linaro/qemu/linux-user/signal.c:385:10: error: > ‘tgt_tmp._sifields._sigchld._utime’ may be used uninitialized in this > function [-Werror=maybe-uninitialized] > __put_user(info->_sifields._sigchld._utime, > ^ > /home/voipio/linaro/qemu/linux-user/signal.c:403:22: note: > ‘tgt_tmp._sifields._sigchld._utime’ was declared here > target_siginfo_t tgt_tmp; > ^ > /home/voipio/linaro/qemu/linux-user/signal.c:383:10: error: > ‘tgt_tmp._sifields._sigchld._status’ may be used uninitialized in this > function [-Werror=maybe-uninitialized] > __put_user(info->_sifields._sigchld._status, > ^ > /home/voipio/linaro/qemu/linux-user/signal.c:403:22: note: > ‘tgt_tmp._sifields._sigchld._status’ was declared here > target_siginfo_t tgt_tmp; > ^ > cc1: all warnings being treated as errors > > These appear to be the architectures where setup_rt_frame isn't implemented. > > > > Signed-off-by: Peter Maydell <peter.mayd...@linaro.org> > > --- > > linux-user/signal.c | 165 > > ++++++++++++++++++++++++++++++++-------------- > > linux-user/syscall_defs.h | 15 +++++ > > 2 files changed, 131 insertions(+), 49 deletions(-) > > > > diff --git a/linux-user/signal.c b/linux-user/signal.c > > index b21d6bf..8ea0cbf 100644 > > --- a/linux-user/signal.c > > +++ b/linux-user/signal.c > > @@ -17,6 +17,7 @@ > > * along with this program; if not, see <http://www.gnu.org/licenses/>. > > */ > > #include "qemu/osdep.h" > > +#include "qemu/bitops.h" > > #include <sys/ucontext.h> > > #include <sys/resource.h> > > > > @@ -274,70 +275,129 @@ static inline void > > host_to_target_siginfo_noswap(target_siginfo_t *tinfo, > > const siginfo_t *info) > > { > > int sig = host_to_target_signal(info->si_signo); > > + int si_code = info->si_code; > > + int si_type; > > tinfo->si_signo = sig; > > tinfo->si_errno = 0; > > tinfo->si_code = info->si_code; > > > > - if (sig == TARGET_SIGILL || sig == TARGET_SIGFPE || sig == > > TARGET_SIGSEGV > > - || sig == TARGET_SIGBUS || sig == TARGET_SIGTRAP) { > > - /* Should never come here, but who knows. The information for > > - the target is irrelevant. */ > > - tinfo->_sifields._sigfault._addr = 0; > > - } else if (sig == TARGET_SIGIO) { > > - tinfo->_sifields._sigpoll._band = info->si_band; > > - tinfo->_sifields._sigpoll._fd = info->si_fd; > > - } else if (sig == TARGET_SIGCHLD) { > > - tinfo->_sifields._sigchld._pid = info->si_pid; > > - tinfo->_sifields._sigchld._uid = info->si_uid; > > - tinfo->_sifields._sigchld._status > > + /* This is awkward, because we have to use a combination of > > + * the si_code and si_signo to figure out which of the union's > > + * members are valid. (Within the host kernel it is always possible > > + * to tell, but the kernel carefully avoids giving userspace the > > + * high 16 bits of si_code, so we don't have the information to > > + * do this the easy way...) We therefore make our best guess, > > + * bearing in mind that a guest can spoof most of the si_codes > > + * via rt_sigqueueinfo() if it likes. > > + * > > + * Once we have made our guess, we record it in the top 16 bits of > > + * the si_code, so that tswap_siginfo() later can use it. > > + * tswap_siginfo() will strip these top bits out before writing > > + * si_code to the guest (sign-extending the lower bits). > > + */ > > + > > + switch (si_code) { > > + case SI_USER: > > + case SI_TKILL: > > + case SI_KERNEL: > > + /* Sent via kill(), tkill() or tgkill(), or direct from the kernel. > > + * These are the only unspoofable si_code values. > > + */ > > + tinfo->_sifields._kill._pid = info->si_pid; > > + tinfo->_sifields._kill._uid = info->si_uid; > > + si_type = QEMU_SI_KILL; > > + break; > > + default: > > + /* Everything else is spoofable. Make best guess based on signal */ > > + switch (sig) { > > + case TARGET_SIGCHLD: > > + tinfo->_sifields._sigchld._pid = info->si_pid; > > + tinfo->_sifields._sigchld._uid = info->si_uid; > > + tinfo->_sifields._sigchld._status > > = host_to_target_waitstatus(info->si_status); > > - tinfo->_sifields._sigchld._utime = info->si_utime; > > - tinfo->_sifields._sigchld._stime = info->si_stime; > > - } else if (sig >= TARGET_SIGRTMIN) { > > - tinfo->_sifields._rt._pid = info->si_pid; > > - tinfo->_sifields._rt._uid = info->si_uid; > > - /* XXX: potential problem if 64 bit */ > > - tinfo->_sifields._rt._sigval.sival_ptr > > + tinfo->_sifields._sigchld._utime = info->si_utime; > > + tinfo->_sifields._sigchld._stime = info->si_stime; > > + si_type = QEMU_SI_CHLD; > > + break; > > + case TARGET_SIGIO: > > + tinfo->_sifields._sigpoll._band = info->si_band; > > + tinfo->_sifields._sigpoll._fd = info->si_fd; > > + si_type = QEMU_SI_POLL; > > + break; > > + default: > > + /* Assume a sigqueue()/mq_notify()/rt_sigqueueinfo() source. */ > > + tinfo->_sifields._rt._pid = info->si_pid; > > + tinfo->_sifields._rt._uid = info->si_uid; > > + /* XXX: potential problem if 64 bit */ > > + tinfo->_sifields._rt._sigval.sival_ptr > > = (abi_ulong)(unsigned long)info->si_value.sival_ptr; > > + si_type = QEMU_SI_RT; > > + break; > > + } > > + break; > > } > > + > > + tinfo->si_code = deposit32(si_code, 16, 16, si_type); > > } > > > > static void tswap_siginfo(target_siginfo_t *tinfo, > > const target_siginfo_t *info) > > { > > - int sig = info->si_signo; > > - tinfo->si_signo = tswap32(sig); > > - tinfo->si_errno = tswap32(info->si_errno); > > - tinfo->si_code = tswap32(info->si_code); > > - > > - if (sig == TARGET_SIGILL || sig == TARGET_SIGFPE || sig == > > TARGET_SIGSEGV > > - || sig == TARGET_SIGBUS || sig == TARGET_SIGTRAP) { > > - tinfo->_sifields._sigfault._addr > > - = tswapal(info->_sifields._sigfault._addr); > > - } else if (sig == TARGET_SIGIO) { > > - tinfo->_sifields._sigpoll._band > > - = tswap32(info->_sifields._sigpoll._band); > > - tinfo->_sifields._sigpoll._fd = > > tswap32(info->_sifields._sigpoll._fd); > > - } else if (sig == TARGET_SIGCHLD) { > > - tinfo->_sifields._sigchld._pid > > - = tswap32(info->_sifields._sigchld._pid); > > - tinfo->_sifields._sigchld._uid > > - = tswap32(info->_sifields._sigchld._uid); > > - tinfo->_sifields._sigchld._status > > - = tswap32(info->_sifields._sigchld._status); > > - tinfo->_sifields._sigchld._utime > > - = tswapal(info->_sifields._sigchld._utime); > > - tinfo->_sifields._sigchld._stime > > - = tswapal(info->_sifields._sigchld._stime); > > - } else if (sig >= TARGET_SIGRTMIN) { > > - tinfo->_sifields._rt._pid = tswap32(info->_sifields._rt._pid); > > - tinfo->_sifields._rt._uid = tswap32(info->_sifields._rt._uid); > > - tinfo->_sifields._rt._sigval.sival_ptr > > - = tswapal(info->_sifields._rt._sigval.sival_ptr); > > + int si_type = extract32(info->si_code, 16, 16); > > + int si_code = sextract32(info->si_code, 0, 16); > > + > > + __put_user(info->si_signo, &tinfo->si_signo); > > + __put_user(info->si_errno, &tinfo->si_errno); > > + __put_user(si_code, &tinfo->si_code); > > + > > + /* We can use our internal marker of which fields in the structure > > + * are valid, rather than duplicating the guesswork of > > + * host_to_target_siginfo_noswap() here. > > + */ > > + switch (si_type) { > > + case QEMU_SI_KILL: > > + __put_user(info->_sifields._kill._pid, > > &tinfo->_sifields._kill._pid); > > + __put_user(info->_sifields._kill._uid, > > &tinfo->_sifields._kill._uid); > > + break; > > + case QEMU_SI_TIMER: > > + __put_user(info->_sifields._timer._timer1, > > + &tinfo->_sifields._timer._timer1); > > + __put_user(info->_sifields._timer._timer2, > > + &tinfo->_sifields._timer._timer2); > > + break; > > + case QEMU_SI_POLL: > > + __put_user(info->_sifields._sigpoll._band, > > + &tinfo->_sifields._sigpoll._band); > > + __put_user(info->_sifields._sigpoll._fd, > > + &tinfo->_sifields._sigpoll._fd); > > + break; > > + case QEMU_SI_FAULT: > > + __put_user(info->_sifields._sigfault._addr, > > + &tinfo->_sifields._sigfault._addr); > > + break; > > + case QEMU_SI_CHLD: > > + __put_user(info->_sifields._sigchld._pid, > > + &tinfo->_sifields._sigchld._pid); > > + __put_user(info->_sifields._sigchld._uid, > > + &tinfo->_sifields._sigchld._uid); > > + __put_user(info->_sifields._sigchld._status, > > + &tinfo->_sifields._sigchld._status); > > + __put_user(info->_sifields._sigchld._utime, > > + &tinfo->_sifields._sigchld._utime); > > + __put_user(info->_sifields._sigchld._stime, > > + &tinfo->_sifields._sigchld._stime); > > + break; > > + case QEMU_SI_RT: > > + __put_user(info->_sifields._rt._pid, &tinfo->_sifields._rt._pid); > > + __put_user(info->_sifields._rt._uid, &tinfo->_sifields._rt._uid); > > + __put_user(info->_sifields._rt._sigval.sival_ptr, > > + &tinfo->_sifields._rt._sigval.sival_ptr); > > + break; > > + default: > > + g_assert_not_reached(); > > } > > } > > > > - > > void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info) > > { > > host_to_target_siginfo_noswap(tinfo, info); > > @@ -505,6 +565,13 @@ int queue_signal(CPUArchState *env, int sig, > > target_siginfo_t *info) > > > > trace_user_queue_signal(env, sig); > > > > + /* Currently all callers define siginfo structures which > > + * use the _sifields._sigfault union member, so we can > > + * set the type here. If that changes we should push this > > + * out so the si_type is passed in by callers. > > + */ > > + info->si_code = deposit32(info->si_code, 16, 16, QEMU_SI_FAULT); > > + > > ts->sync_signal.info = *info; > > ts->sync_signal.pending = sig; > > /* signal that a new signal is pending */ > > diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h > > index 34af15a..124754f 100644 > > --- a/linux-user/syscall_defs.h > > +++ b/linux-user/syscall_defs.h > > @@ -673,6 +673,21 @@ typedef struct { > > > > #define TARGET_SI_PAD_SIZE ((TARGET_SI_MAX_SIZE - TARGET_SI_PREAMBLE_SIZE) > > / sizeof(int)) > > > > +/* Within QEMU the top 16 bits of si_code indicate which of the parts of > > + * the union in target_siginfo is valid. This only applies between > > + * host_to_target_siginfo_noswap() and tswap_siginfo(); it does not > > + * appear either within host siginfo_t or in target_siginfo structures > > + * which we get from the guest userspace program. (The Linux kernel > > + * does a similar thing with using the top bits for its own internal > > + * purposes but not letting them be visible to userspace.) > > + */ > > +#define QEMU_SI_KILL 0 > > +#define QEMU_SI_TIMER 1 > > +#define QEMU_SI_POLL 2 > > +#define QEMU_SI_FAULT 3 > > +#define QEMU_SI_CHLD 4 > > +#define QEMU_SI_RT 5 > > + > > typedef struct target_siginfo { > > #ifdef TARGET_MIPS > > int si_signo; > > -- > > 1.9.1 > >