Subject: siginfo structure in 64-bit kernel

From: Carsten Langgaard <[email-masked]>
Date: Wed, 07 Aug 2002 11:54:08 +0200

The siginfo structure is containing longs, which isn't consistent
between o32 and n64.
So we need a routine to convert siginfo from 64-bit to 32-bit, when we
are running a 64-bit kernel on o32 userland.
Please take a look at the patch below.

/Carsten

--
_    _ ____  ___   Carsten Langgaard   Mailto:[email-masked]
|\  /|||___)(___   MIPS Denmark        Direct: +45 4486 5527
| \/ |||    ____)  Lautrupvang 4B      Switch: +45 4486 5555
  TECHNOLOGIES     2750 Ballerup       Fax...: +45 4486 5556
                   Denmark             http://www.mips.com

Index: arch/mips64/kernel/signal32.c
===================================================================
RCS file: /cvs/linux/arch/mips64/kernel/signal32.c,v
retrieving revision 1.20.2.7
diff -u -r1.20.2.7 signal32.c
--- arch/mips64/kernel/signal32.c	2002/08/05 23:53:36	1.20.2.7
+++ arch/mips64/kernel/signal32.c	2002/08/07 08:52:00
@@ -360,13 +360,55 @@
 	sigset_t sf_mask;
 };
 
-struct rt_sigframe {
+struct rt_sigframe32 {
 	u32 rs_ass[4];			/* argument save space for o32 */
 	u32 rs_code[2];			/* signal trampoline */
-	struct siginfo rs_info;
+	struct siginfo32 rs_info;
 	struct ucontext rs_uc;
 };
 
+static int copy_siginfo_to_user32(siginfo_t32 *to, siginfo_t *from)
+{
+	int err;
+
+	if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t32)))
+		return -EFAULT;
+
+	/* If you change siginfo_t structure, please be sure
+	   this code is fixed accordingly.
+	   It should never copy any pad contained in the structure
+	   to avoid security leaks, but must copy the generic
+	   3 ints plus the relevant union member.
+	   This routine must convert siginfo from 64bit to 32bit as well
+	   at the same time.  */
+	err = __put_user(from->si_signo, &to->si_signo);
+	err |= __put_user(from->si_errno, &to->si_errno);
+	err |= __put_user((short)from->si_code, &to->si_code);
+	if (from->si_code < 0)
+		err |= __copy_to_user(&to->_sifields._pad, &from->_sifields._pad, SI_PAD_SIZE);
+	else {
+		switch (from->si_code >> 16) {
+		case __SI_CHLD >> 16:
+			err |= __put_user(from->si_utime, &to->si_utime);
+			err |= __put_user(from->si_stime, &to->si_stime);
+			err |= __put_user(from->si_status, &to->si_status);
+		default:
+			err |= __put_user(from->si_pid, &to->si_pid);
+			err |= __put_user(from->si_uid, &to->si_uid);
+			break;
+		case __SI_FAULT >> 16:
+			err |= __put_user((long)from->si_addr, &to->si_addr);
+			break;
+		case __SI_POLL >> 16:
+			err |= __put_user(from->si_band, &to->si_band);
+			err |= __put_user(from->si_fd, &to->si_fd);
+			break;
+		/* case __SI_RT: This is not generated by the kernel as of now.  */
+		}
+	}
+	return err;
+}
+
 asmlinkage void sys32_sigreturn(abi64_no_regargs, struct pt_regs regs)
 {
 	struct sigframe *frame;
@@ -405,11 +447,11 @@
 
 asmlinkage void sys32_rt_sigreturn(abi64_no_regargs, struct pt_regs regs)
 {
-	struct rt_sigframe *frame;
+	struct rt_sigframe32 *frame;
 	sigset_t set;
 	stack_t st;
 
-	frame = (struct rt_sigframe *) regs.regs[29];
+	frame = (struct rt_sigframe32 *) regs.regs[29];
 	if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
 		goto badframe;
 	if (__copy_from_user(&set, &frame->rs_uc.uc_sigmask, sizeof(set)))
@@ -588,7 +630,7 @@
 				  struct pt_regs *regs, int signr,
 				  sigset_t *set, siginfo_t *info)
 {
-	struct rt_sigframe *frame;
+	struct rt_sigframe32 *frame;
 	int err = 0;
 
 	frame = get_sigframe(ka, regs, sizeof(*frame));
@@ -613,8 +655,8 @@
 		flush_cache_sigtramp((unsigned long) frame->rs_code);
 	}
 
-	/* Create siginfo.  */
-	err |= __copy_to_user(&frame->rs_info, info, sizeof(*info));
+	/* Convert (siginfo_t -> siginfo_t32) and copy to user. */
+	err |= copy_siginfo_to_user32(&frame->rs_info,info);
 
 	/* Create the ucontext.  */
 	err |= __put_user(0, &frame->rs_uc.uc_flags);
@@ -639,7 +681,7 @@
 	 *   a2 = pointer to ucontext
 	 *
 	 * $25 and c0_epc point to the signal handler, $29 points to
-	 * the struct rt_sigframe.
+	 * the struct rt_sigframe32.
 	 */
 	regs->regs[ 4] = signr;
 	regs->regs[ 5] = (unsigned long) &frame->rs_info;
Index: include/asm-mips64/siginfo.h
===================================================================
RCS file: /cvs/linux/include/asm-mips64/siginfo.h,v
retrieving revision 1.6.2.2
diff -u -r1.6.2.2 siginfo.h
--- include/asm-mips64/siginfo.h	2002/08/05 23:53:39	1.6.2.2
+++ include/asm-mips64/siginfo.h	2002/08/07 08:52:16
@@ -18,11 +18,21 @@
 	void *sival_ptr;
 } sigval_t;
 
+#ifdef __KERNEL__
+
+typedef union sigval32 {
+	int sival_int;
+	u32 sival_ptr;
+} sigval_t32;
+
+#endif /* __KERNEL__ */
+
 /* This structure matches IRIX 32/n32 ABIs for binary compatibility but
    has Linux extensions.  */
 
 #define SI_MAX_SIZE	128
-#define SI_PAD_SIZE	((SI_MAX_SIZE/sizeof(int)) - 3)
+#define SI_PAD_SIZE	((SI_MAX_SIZE/sizeof(int)) - 4)
+#define SI_PAD_SIZE32	((SI_MAX_SIZE/sizeof(int)) - 3)
 
 typedef struct siginfo {
 	int si_signo;
@@ -81,6 +91,67 @@
 
 	} _sifields;
 } siginfo_t;
+
+#ifdef __KERNEL__
+
+typedef struct siginfo32 {
+	int si_signo;
+	int si_errno;
+	int si_code;
+	union {
+		int _pad[SI_PAD_SIZE32];
+
+		/* kill() */
+		struct {
+			__kernel_pid_t32 _pid;	/* sender's pid */
+			__kernel_uid_t32 _uid;	/* sender's uid */
+		} _kill;
+
+		/* SIGCHLD */
+		struct {
+			__kernel_pid_t32 _pid;	/* which child */
+			__kernel_uid_t32 _uid;	/* sender's uid */
+			__kernel_clock_t32 _utime;
+			int _status;		/* exit code */
+			__kernel_clock_t32 _stime;
+		} _sigchld;
+
+		/* IRIX SIGCHLD */
+		struct {
+			__kernel_pid_t32 _pid;	/* which child */
+			__kernel_clock_t32 _utime;
+			int _status;		/* exit code */
+			__kernel_clock_t32 _stime;
+		} _irix_sigchld;
+
+		/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
+		struct {
+			u32 _addr; /* faulting insn/memory ref. */
+		} _sigfault;
+
+		/* SIGPOLL, SIGXFSZ (To do ...)  */
+		struct {
+			int _band;	/* POLL_IN, POLL_OUT, POLL_MSG */
+			int _fd;
+		} _sigpoll;
+
+		/* POSIX.1b timers */
+		struct {
+			unsigned int _timer1;
+			unsigned int _timer2;
+		} _timer;
+
+		/* POSIX.1b signals */
+		struct {
+			__kernel_pid_t32 _pid;	/* sender's pid */
+			__kernel_uid_t32 _uid;	/* sender's uid */
+			sigval_t32 _sigval;
+		} _rt;
+
+	} _sifields;
+} siginfo_t32;
+
+#endif /* __KERNEL__ */
 
 /*
  * How these fields are to be accessed.

---

From: "Maciej W. Rozycki" <[email-masked]>
Date: Wed, 7 Aug 2002 13:49:40 +0200 (MET DST)

> are running a 64-bit kernel on o32 userland.

 Yep, I've noticed we don't get it quite right recently, too.

> Please take a look at the patch below.

 It looks good in principle, but please use signed types for addresses.
I'll check the patch at run-time later.

-- 
+  Maciej W. Rozycki, Technical University of Gdansk, Poland   +
+--------------------------------------------------------------+
+        e-mail: [email-masked], PGP key available        +

---

From: "Maciej W. Rozycki" <[email-masked]>
Date: Thu, 8 Aug 2002 16:26:45 +0200 (MET DST)

> I'll check the patch at run-time later.

 I checked the patch and discovered you somehow made the order of struct
members wrong.  Here is an updated version that works for me.  It includes
both the ordering fix and unsigned type changes I suggested before.

 This version should be OK to apply.

patch-mips-2.4.19-rc1-20020807-carsten-signal32-3
diff -up --recursive --new-file linux-mips-2.4.19-rc1-20020807.macro/arch/mips64/kernel/signal32.c linux-mips-2.4.19-rc1-20020807/arch/mips64/kernel/signal32.c
--- linux-mips-2.4.19-rc1-20020807.macro/arch/mips64/kernel/signal32.c	2002-08-06 02:57:36.000000000 +0000
+++ linux-mips-2.4.19-rc1-20020807/arch/mips64/kernel/signal32.c	2002-08-08 00:20:04.000000000 +0000
@@ -360,13 +360,55 @@ struct sigframe {
 	sigset_t sf_mask;
 };
 
-struct rt_sigframe {
+struct rt_sigframe32 {
 
@@ -405,11 +447,11 @@ badframe:

@@ -588,7 +630,7 @@ static void inline setup_rt_frame(struct
 
 	frame = get_sigframe(ka, regs, sizeof(*frame));
@@ -613,8 +655,8 @@ static void inline setup_rt_frame(struct
 		flush_cache_sigtramp((unsigned long) frame->rs_code);
 	}
 
-	/* Create siginfo.  */
-	err |= __copy_to_user(&frame->rs_info, info, sizeof(*info));
+	/* Convert (siginfo_t -> siginfo_t32) and copy to user. */
+	err |= copy_siginfo_to_user32(&frame->rs_info, info);
 
 	/* Create the ucontext.  */
 	err |= __put_user(0, &frame->rs_uc.uc_flags);
@@ -639,7 +681,7 @@ static void inline setup_rt_frame(struct
 	 *   a2 = pointer to ucontext
 	 *
 	 * $25 and c0_epc point to the signal handler, $29 points to
-	 * the struct rt_sigframe.
+	 * the struct rt_sigframe32.
 	 */
 	regs->regs[ 4] = signr;
 	regs->regs[ 5] = (unsigned long) &frame->rs_info;
diff -up --recursive --new-file linux-mips-2.4.19-rc1-20020807.macro/include/asm-mips64/siginfo.h linux-mips-2.4.19-rc1-20020807/include/asm-mips64/siginfo.h
--- linux-mips-2.4.19-rc1-20020807.macro/include/asm-mips64/siginfo.h	2002-08-06 02:58:32.000000000 +0000
+++ linux-mips-2.4.19-rc1-20020807/include/asm-mips64/siginfo.h	2002-08-08 07:14:29.000000000 +0000
@@ -18,11 +18,21 @@ typedef union sigval {
 	void *sival_ptr;
 } sigval_t;
 
+#ifdef __KERNEL__
+
+	s32 sival_ptr;
 
 #define SI_MAX_SIZE	128
 
 typedef struct siginfo {
 	int si_signo;
@@ -82,6 +92,68 @@ typedef struct siginfo {
 	} _sifields;
 } siginfo_t;
 
+#ifdef __KERNEL__
+
+			s32 _addr; /* faulting insn/memory ref. */

---

From: Carsten Langgaard <[email-masked]>
Date: Thu, 08 Aug 2002 22:02:35 +0200

"Maciej W. Rozycki" wrote:

> members wrong.

Good spotted, that's what happens when MIPS is the only one that put 'si_code' before 'si_errno' in the structure.

>  Here is an updated version that works for me.  It includes

With your sign changes we are doing things a little bit different than others, I know that's not really an argument, but does the unsigned types not work for you ?

---

From: "Maciej W. Rozycki" <[email-masked]>
Date: Fri, 9 Aug 2002 10:39:57 +0200 (MET DST)

On Thu, 8 Aug 2002, Carsten Langgaard wrote:

> Good spotted, that's what happens when MIPS is the only one that put
> 'si_code' before 'si_errno' in the structure. 

 That's why I'm always using MIPS-specific definitions as a reference
first.  The actual reason of the differences is the SysV ABI supplement
for MIPS which defines things a bit differently than the others here and
there, often with no justified reason (well, the reason is really Irix,
but then again, there is no justified reason for Irix to be different
there).

> With your sign changes we are doing things a little bit different than
> others, I know that's not really an argument, but does the unsigned
> types not work for you ? 

 Others are different here, actually.  With a signed type if a 32-bit
address is cast to "long" or "void *" implicitly (the latter is normally
spotted by the compiler and marked with a warning; the former is usually
silent), it becomes a valid 64-bit address that still refers to the same
location.  With an unsigned one it doesn't work for KSEG addresses
anymore.  That's because addresses are signed on MIPS -- 32-bit addresses
are sign-extended (as opposed to zero-extended) by the hardware the 32-bit
address space is in the middle of the 64-bit one (and not at the bottom as
in the zero-extension variant). 

 I suppose the unsigned types would usually work if casts were applied
carefully everywhere.  I'd prefer code to work automatically, though,
without corner cases triggered every now and then.