tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Softfloat userland needing to properly deliver SIGFPE traps



After Christos recently added sigqueue and friends to -current, I tried to
use them to fix a long standing softfloat userland problem. One variant of
the problem shows up in sparc64 atf test runs (sparc64 uses softfloat
for 128 bit long double): the userland software, and our atf tests, expect
to get proper details about the SIGFPE it caught, but softloat only did a
raise(SIGFPE), so no siginfo is available.

The userland change looked straight forward:

Index: softfloat-specialize
===================================================================
RCS file: /cvsroot/src/lib/libc/softfloat/softfloat-specialize,v
retrieving revision 1.4
diff -c -u -p -r1.4 softfloat-specialize
--- softfloat-specialize        26 Sep 2004 21:13:27 -0000      1.4
+++ softfloat-specialize        14 Jan 2011 09:40:15 -0000
@@ -56,11 +59,26 @@ should be simply `float_exception_flags 
 fp_except float_exception_mask = 0;
 void float_raise( fp_except flags )
 {
+    siginfo_t info;
 
     float_exception_flags |= flags;
 
     if ( flags & float_exception_mask ) {
-       raise( SIGFPE );
+       memset(&info, 0, sizeof info);
+       info.si_signo = SIGFPE;
+       info.si_pid = getpid();
+       info.si_uid = geteuid();
+       if (flags & float_flag_underflow)
+           info.si_code = FPE_FLTUND;
+       else if (flags & float_flag_overflow)
+           info.si_code = FPE_FLTOVF;
+       else if (flags & float_flag_divbyzero)
+           info.si_code = FPE_FLTDIV;
+       else if (flags & float_flag_invalid)
+           info.si_code = FPE_FLTINV;
+       else if (flags & float_flag_inexact)
+           info.si_code = FPE_FLTRES;
+       sigqueueinfo(getpid(), &info);
     }
 }


But: looking at the kernel code, this is not allowed. Only SI_USER and
SI_QUEUE request are passed through. For testing, I disabled this
security check like this:
 
Index: sys_sig.c
===================================================================
RCS file: /cvsroot/src/sys/kern/sys_sig.c,v
retrieving revision 1.30
diff -c -u -p -r1.30 sys_sig.c
--- sys_sig.c   10 Jan 2011 04:39:18 -0000      1.30
+++ sys_sig.c   14 Jan 2011 09:40:38 -0000
@@ -229,14 +229,16 @@ kill1(struct lwp *l, pid_t pid, ksiginfo
        if (ksi->ksi_uid != kauth_cred_geteuid(l->l_cred))
                return EPERM;
 
-       switch (ksi->ksi_code) {
-       case SI_USER:
-       case SI_QUEUE:
-               break;
-       default:
-               return EPERM;
+       if (ksi->ksi_signo != SIGFPE) {
+               switch (ksi->ksi_code) {
+               case SI_USER:
+               case SI_QUEUE:
+                       break;
+               default:
+                       return EPERM;
+               }
        }
-               
+
        if (pid > 0) {
                /* kill single process */
                mutex_enter(proc_lock);


This change makes it work for me, but of course it is a horrible hack, not
acceptable for commit. We need to have the possibility to SI_USER/
SI_QUEUE a SIGFPE, but given the mostly union content of ksiginfo, I
don't see an obvious way how to do it properly.

I'd like to suggest a special SI_SELFSIGFPE ksi_code, which overrides
the pid/uid tests, only allows sending to the same process, and encodes
the final (kernel internal) ksi_code as ksi_signo, while passing on the
rest of ksiginfo untouched (so userland could, if possible, fill in
struct fault).

Userland call would look like this:

+       memset(&info, 0, sizeof info);
+       info.si_code = SI_SELFSIGFPE;
+       if (flags & float_flag_underflow)
+           info.si_signo = FPE_FLTUND;
+       else if (flags & float_flag_overflow)
+           info.si_signo = FPE_FLTOVF;
+       else if (flags & float_flag_divbyzero)
+           info.si_signo = FPE_FLTDIV;
+       else if (flags & float_flag_invalid)
+           info.si_signo = FPE_FLTINV;
+       else if (flags & float_flag_inexact)
+           info.si_signo = FPE_FLTRES;
+       sigqueueinfo(getpid(), &info);

and kernel would move si_signo to ksi_code, set signo to SIGFPE, and pass
the remaining siginfo on untouched.

Still a hack, but I don't have better ideas (besides creating another special
purpose syscall for this).

Martin


Home | Main Index | Thread Index | Old Index