With a little help from Paul yesterday, I was able to come up with a
scheme for detecting bad pointers passed to system calls in linux-user
mode. This is used to return EFAULT as would be done on a real kernel.

The attached patch is very preliminary, but shows how it can be done.
I'm sending it now to solicit comments.

The patch currently just add a seperate call to validate the address. Per
yesterdays discussion, the checking should be folded into lock_user(),
but it's not a trivial drop in as lock_user() and lock_user_struct() are
used in different ways in different places, and none of them are actually
checking a return value. I'm still thinking on how best to accomplish
this part.

The end result, is that the tests in LTPs msg* tests that try to
generate EFAULT can now do so (and thus PASS).


                                Stuart

Stuart R. Anderson                               [EMAIL PROTECTED]
Network & Software Engineering                   http://www.netsweng.com/
1024D/37A79149:                                  0791 D3B8 9A4C 2CDC A31F
                                                 BD03 0A62 E534 37A7 9149
Index: qemu/exec.c
===================================================================
--- qemu.orig/exec.c	2007-03-22 17:18:32.000000000 -0400
+++ qemu/exec.c	2007-03-22 17:42:30.000000000 -0400
@@ -1785,6 +1785,29 @@
     spin_unlock(&tb_lock);
 }
 
+int page_check_range(target_ulong start, target_ulong len, int flags)
+{
+    PageDesc *p;
+    target_ulong end;
+    target_ulong addr;
+
+    end = TARGET_PAGE_ALIGN(start+len); /* must do before we loose bits in the next step */
+    start = start & TARGET_PAGE_MASK;
+
+    if( end < start ) return EFAULT;  /* we've wrapped around */
+    for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) {
+        p = page_find(addr >> TARGET_PAGE_BITS);
+	if( !p ) return EFAULT;
+	if( !(p->flags & PAGE_VALID) ) return EFAULT;
+
+        if (!(p->flags & PAGE_READ) &&
+            (flags & PAGE_READ) ) return EFAULT;
+        if (!(p->flags & PAGE_WRITE) &&
+            (flags & PAGE_WRITE) ) return EFAULT;
+    }
+    return 0;
+}
+
 /* called from signal handler: invalidate the code and unprotect the
    page. Return TRUE if the fault was succesfully handled. */
 int page_unprotect(target_ulong address, unsigned long pc, void *puc)
Index: qemu/cpu-all.h
===================================================================
--- qemu.orig/cpu-all.h	2007-03-22 17:18:32.000000000 -0400
+++ qemu/cpu-all.h	2007-03-22 17:19:10.000000000 -0400
@@ -689,6 +689,7 @@
 int page_get_flags(target_ulong address);
 void page_set_flags(target_ulong start, target_ulong end, int flags);
 void page_unprotect_range(target_ulong data, target_ulong data_size);
+int page_check_range(target_ulong start, target_ulong len, int flags);
 
 #define SINGLE_CPU_DEFINES
 #ifdef SINGLE_CPU_DEFINES
Index: qemu/linux-user/syscall.c
===================================================================
--- qemu.orig/linux-user/syscall.c	2007-03-22 17:19:00.000000000 -0400
+++ qemu/linux-user/syscall.c	2007-03-22 17:26:17.000000000 -0400
@@ -1287,12 +1287,15 @@
   target_ulong __unused5;
 };
 
-static inline void target_to_host_msqid_ds(struct msqid_ds *host_md,
-                                          target_ulong target_addr)
+/* static inline */ long target_to_host_msqid_ds(struct msqid_ds *host_md,
+                                          target_ulong target_addr,
+                                          int pg_access)
 {
+    long ret = 0;
     struct target_msqid_ds *target_md;
 
     lock_user_struct(target_md, target_addr, 1);
+    if( ret=page_check_range(target_md,sizeof(*target_md),pg_access) ) return -ret;
     target_to_host_ipc_perm(&(host_md->msg_perm),target_addr);
     host_md->msg_stime = tswapl(target_md->msg_stime);
     host_md->msg_rtime = tswapl(target_md->msg_rtime);
@@ -1303,9 +1306,10 @@
     host_md->msg_lspid = tswapl(target_md->msg_lspid);
     host_md->msg_lrpid = tswapl(target_md->msg_lrpid);
     unlock_user_struct(target_md, target_addr, 0);
+    return ret;
 }
 
-static inline void host_to_target_msqid_ds(target_ulong target_addr,
+/* static inline */ void host_to_target_msqid_ds(target_ulong target_addr,
                                            struct msqid_ds *host_md)
 {
     struct target_msqid_ds *target_md;
@@ -1323,17 +1327,22 @@
     unlock_user_struct(target_md, target_addr, 1);
 }
 
-static inline long do_msgctl(long first, long second, long ptr)
+/* static inline */ long do_msgctl(long first, long second, long ptr)
 {
     struct msqid_ds dsarg;
     int cmd = second&0xff;
     long ret = 0;
     switch( cmd ) {
     case IPC_STAT:
+        if( ret=target_to_host_msqid_ds(&dsarg,ptr,PAGE_WRITE) ) return -ret;
+        ret = get_errno(msgctl(first, cmd, &dsarg));
+        host_to_target_msqid_ds(ptr,&dsarg);
+	break;
     case IPC_SET:
-        target_to_host_msqid_ds(&dsarg,ptr);
+        if( ret=target_to_host_msqid_ds(&dsarg,ptr,PAGE_READ) ) return -ret;
         ret = get_errno(msgctl(first, cmd, &dsarg));
         host_to_target_msqid_ds(ptr,&dsarg);
+	break;
     default:
         ret = get_errno(msgctl(first, cmd, &dsarg));
     }
@@ -1345,13 +1354,14 @@
 	char	mtext[1];
 };
 
-static inline long do_msgsnd(long msqid, long msgp, long msgsz, long msgflg)
+/* static inline */ long do_msgsnd(long msqid, long msgp, long msgsz, long msgflg)
 {
     struct target_msgbuf *target_mb;
     struct msgbuf *host_mb;
     long ret = 0;
 
     lock_user_struct(target_mb,msgp,0);
+    if( ret=page_check_range(target_mb,sizeof(long)+msgsz,PAGE_READ) ) return -ret;
     host_mb = malloc(msgsz+sizeof(long));
     host_mb->mtype = tswapl(target_mb->mtype);
     memcpy(host_mb->mtext,target_mb->mtext,msgsz);
@@ -1362,13 +1372,14 @@
     return ret;
 }
 
-static inline long do_msgrcv(long msqid, long msgp, long msgsz, long msgtype, long msgflg)
+/* static inline */ long do_msgrcv(long msqid, long msgp, long msgsz, long msgtype, long msgflg)
 {
     struct target_msgbuf *target_mb;
     struct msgbuf *host_mb;
     long ret = 0;
 
     lock_user_struct(target_mb,msgp,0);
+    if( ret=page_check_range(target_mb,sizeof(long)+msgsz,PAGE_WRITE) ) return -ret;
     host_mb = malloc(msgsz+sizeof(long));
     ret = get_errno(msgrcv(msqid, host_mb, msgsz, 1, msgflg));
     if( ret > 0 )
@@ -1381,7 +1392,7 @@
 }
 
 /* ??? This only works with linear mappings.  */
-static long do_ipc(long call, long first, long second, long third,
+/* static */ long do_ipc(long call, long first, long second, long third,
 		   long ptr, long fifth)
 {
     int version;
_______________________________________________
Qemu-devel mailing list
Qemu-devel@nongnu.org
http://lists.nongnu.org/mailman/listinfo/qemu-devel

Reply via email to