Subject: per page verified exec
To: None <tech-kern@netbsd.org>
From: Brett Lymn <blymn@baesystems.com.au>
List: tech-kern
Date: 09/10/2006 20:56:13
--x+6KMIRAuhnl3hBn
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline


Folks,

Due to the changes in the fileassoc code the per page verified exec
code has become much simpler.  Attached are the diffs to enable
per-page verified exec.  If there are no objections, I will commit
this code soon.

-- 
Brett Lymn

--x+6KMIRAuhnl3hBn
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="veriexec_per_page.diff"

Index: dev/verified_exec.c
===================================================================
RCS file: /cvsroot/src/sys/dev/verified_exec.c,v
retrieving revision 1.43
diff -u -r1.43 verified_exec.c
--- dev/verified_exec.c	5 Sep 2006 13:02:16 -0000	1.43
+++ dev/verified_exec.c	10 Sep 2006 11:18:00 -0000
@@ -168,7 +168,6 @@
 	if (veriexec_strict > VERIEXEC_LEARNING) {
 		log(LOG_WARNING, "Veriexec: Strict mode, modifying tables not "
 		    "permitted.\n");
-
 		return (EPERM);
 	}
 
@@ -184,11 +183,12 @@
 
 	case VERIEXEC_DELETE:
 		error = veriexec_delete((struct veriexec_delete_params *)data,
-		    l);
+					l);
 		break;
 
 	case VERIEXEC_QUERY:
-		error = veriexec_query((struct veriexec_query_params *)data, l);
+		error = veriexec_query((struct veriexec_query_params *)data,
+				       l);
 		break;
 
 	default:
@@ -297,18 +297,13 @@
 		     (memcmp(hh->fp, params->fingerprint,
 		      min(hh->ops->hash_len, params->size))
 		      != 0)) ? "different" : "same"));
-
 			error = 0;
 			goto out;
 	}
 
-	e = malloc(sizeof(*e), M_TEMP, M_WAITOK);
+	e = malloc(sizeof(*e), M_TEMP, M_WAITOK | M_ZERO);
 	e->type = params->type;
-	e->status = FINGERPRINT_NOTEVAL;
-	e->page_fp = NULL;
-	e->page_fp_status = PAGE_FP_NONE;
-	e->npages = 0;
-	e->last_page_size = 0;
+	veriexec_purge(e);
 	if ((e->ops = veriexec_find_ops(params->fp_type)) == NULL) {
 		free(e, M_TEMP);
 		log(LOG_ERR, "Veriexec: Invalid or unknown fingerprint type "
Index: kern/kern_verifiedexec.c
===================================================================
RCS file: /cvsroot/src/sys/kern/kern_verifiedexec.c,v
retrieving revision 1.66
diff -u -r1.66 kern_verifiedexec.c
--- kern/kern_verifiedexec.c	11 Aug 2006 19:17:47 -0000	1.66
+++ kern/kern_verifiedexec.c	10 Sep 2006 11:18:02 -0000
@@ -56,6 +56,7 @@
 #include <crypto/sha2/sha2.h>
 #include <crypto/ripemd160/rmd160.h>
 #include <sys/md5.h>
+#include <uvm/uvm.h>
 #include <uvm/uvm_extern.h>
 #include <sys/fileassoc.h>
 #include <sys/kauth.h>
@@ -68,6 +69,9 @@
 
 const struct sysctlnode *veriexec_count_node;
 
+static void
+veriexec_release_pages(int npages, int error, struct vm_page **pps,
+		       struct vnode *vp);
 int veriexec_hook;
 
 /* Veriexecs table of hash types and their associated information. */
@@ -139,9 +143,10 @@
 
 	/* Register a fileassoc for Veriexec. */
 	veriexec_hook = fileassoc_register("veriexec", veriexec_clear);
+
 #ifdef DIAGNOSTIC
 	if (veriexec_hook == FILEASSOC_INVAL)
-		panic("Veriexec: Can't register meta-hook");
+		panic("Veriexec: Can't register fileassoc");
 #endif /* DIAGNOSTIC */
 
 	LIST_INIT(&veriexec_ops_list);
@@ -236,12 +241,10 @@
 	if (error)
 		return (error);
 
-#if 0 /* XXX - for now */
 	if ((vfe->type & VERIEXEC_UNTRUSTED) &&
 	    (vfe->page_fp_status == PAGE_FP_NONE))
 		do_perpage = 1;
 	else
-#endif
 		do_perpage = 0;
 
 	ctx = (void *) malloc(vfe->ops->context_size, M_TEMP, M_WAITOK);
@@ -281,6 +284,9 @@
 			if (do_perpage) {
 				free(vfe->page_fp, M_TEMP);
 				vfe->page_fp = NULL;
+				vfe->page_fp_status = PAGE_FP_NONE;
+				/* XXX: make NOMATCH */
+				vfe->status = FINGERPRINT_NOTEVAL;
 			}
 
 			goto bad;
@@ -413,8 +419,7 @@
 	/* Evaluate fingerprint if needed. */
 	error = 0;
 	digest = NULL;
-	if ((vfe->status == FINGERPRINT_NOTEVAL) ||
-	    (vfe->type & VERIEXEC_UNTRUSTED)) {
+	if (vfe->status == FINGERPRINT_NOTEVAL) {
 		/* Calculate fingerprint for on-disk file. */
 		digest = (u_char *) malloc(vfe->ops->hash_len, M_TEMP,
 					   M_WAITOK);
@@ -566,6 +571,85 @@
 	return (error);
 }
 
+int
+veriexec_block_verify(struct vnode *vp, struct vm_page **pps,
+    voff_t offset, int *npages)
+{
+	struct veriexec_file_entry *vfe = NULL;
+	voff_t offidx, i;
+	int error;
+
+	error = 0;
+
+	vfe = veriexec_lookup(vp);
+	if ((vfe == NULL) || (vfe->page_fp == NULL)
+	    || ((vfe->type & VERIEXEC_UNTRUSTED) == 0)
+	    || ((vfe->type & VERIEXEC_UNTRUSTED) &&
+		(vfe->page_fp_status == PAGE_FP_NONE))) {
+		return (0);
+	}
+
+	offidx = (offset >> PAGE_SHIFT);
+	for (i = 0; i < *npages; i++) {
+		if ((pps[i] == NULL) || (pps[i] == PGO_DONTCARE)) {
+			continue;
+		}
+
+		error = veriexec_page_verify(vfe, pps[i], i + offidx, curlwp);
+
+		if (error) {
+			veriexec_release_pages(*npages, error, pps, vp);
+			*npages = 0;
+			return (error);
+		}
+	}
+
+	return (0);
+}
+
+/*
+ * Release uvm pages - called if an error occurs during page verification.
+ */
+static void
+veriexec_release_pages(int npages, int error, struct vm_page **pps,
+		       struct vnode *vp)
+{
+	struct uvm_object *uobj = &vp->v_uobj;
+	int i;
+	UVMHIST_FUNC("veriexec_release_pages"); UVMHIST_CALLED(ubchist);
+
+	/*
+	 * On error, release all the pages requested,
+	 * the page in may have worked but we are not
+	 * interested if the fingerprinting fails.
+	 */
+	simple_lock(&uobj->vmobjlock);
+	for (i = 0; i < npages; i++) {
+		if ((pps[i] == NULL) || (pps[i] == PGO_DONTCARE)) {
+			continue;
+		}
+		UVMHIST_LOG(ubchist, "veriexec examining pg %p flags 0x%x",
+			    pps[i], pps[i]->flags, 0,0);
+		pps[i]->flags |= PG_RELEASED;
+	}
+	uvm_lock_pageq();
+	for (i = 0; i < npages; i++) {
+		if ((pps[i] == NULL) || (pps[i] == PGO_DONTCARE))
+			continue;
+		pmap_clear_reference(pps[i]);
+		uvm_pagedeactivate(pps[i]);
+		if (pps[i]->flags & PG_WANTED) {
+			wakeup(pps[i]);
+			pps[i]->flags &= ~(PG_WANTED);
+		}
+	}
+	uvm_page_unbusy(pps, npages);
+	uvm_unlock_pageq();
+	simple_unlock(&uobj->vmobjlock);
+	UVMHIST_LOG(ubchist, "veriexec returning",
+		    0,0,0,0);
+}
+
 /*
  * Veriexec remove policy code.
  */
@@ -595,7 +679,7 @@
 	vte = veriexec_tblfind(vp);
 #ifdef DIAGNOSTIC
 	if (vte == NULL)
-		panic("Fileassoc: Inconsistency during entry removel");
+		panic("Fileassoc: Inconsistency during entry removal");
 #endif /* DIAGNOSTIC */
 
 	vte->vte_count--;
@@ -696,6 +780,13 @@
 veriexec_purge(struct veriexec_file_entry *vfe)
 {
 	vfe->status = FINGERPRINT_NOTEVAL;
+	vfe->page_fp_status = PAGE_FP_NONE;
+	vfe->npages = 0;
+	vfe->last_page_size = 0;
+
+	if (vfe->page_fp != NULL)
+		free(vfe->page_fp, M_TEMP);
+	vfe->page_fp = NULL;
 }
 
 /*
Index: sys/verified_exec.h
===================================================================
RCS file: /cvsroot/src/sys/sys/verified_exec.h,v
retrieving revision 1.39
diff -u -r1.39 verified_exec.h
--- sys/verified_exec.h	11 Aug 2006 19:17:47 -0000	1.39
+++ sys/verified_exec.h	10 Sep 2006 11:18:03 -0000
@@ -180,17 +180,17 @@
 int veriexec_add_fp_ops(struct veriexec_fp_ops *);
 void veriexec_init_fp_ops(void);
 struct veriexec_fp_ops *veriexec_find_ops(u_char *name);
-int veriexec_fp_calc(struct lwp *, struct vnode *, struct veriexec_file_entry *,
-    u_char *);
+int veriexec_fp_calc(struct lwp *, struct vnode *,
+		     struct veriexec_file_entry *, u_char *);
 int veriexec_fp_cmp(struct veriexec_fp_ops *, u_char *, u_char *);
 struct veriexec_table_entry *veriexec_tblfind(struct vnode *);
 struct veriexec_file_entry *veriexec_lookup(struct vnode *);
 int veriexec_hashadd(struct vnode *, struct veriexec_file_entry *);
 int veriexec_verify(struct lwp *, struct vnode *,
     const u_char *, int, struct veriexec_file_entry **);
-int veriexec_page_verify(struct veriexec_file_entry *, struct vm_page *, size_t,
-    struct lwp *);
-int veriexec_removechk(struct vnode *, const char *, struct lwp *l);
+int veriexec_page_verify(struct veriexec_file_entry *, struct vm_page *,
+			 size_t, struct lwp *);
+int veriexec_removechk(struct vnode *, const char *, struct lwp *);
 int veriexec_renamechk(struct vnode *, const char *, struct vnode *,
     const char *, struct lwp *);
 void veriexec_report(const u_char *, const u_char *, struct lwp *, int);
@@ -201,7 +201,8 @@
 void veriexec_clear(void *, int);
 void veriexec_purge(struct veriexec_file_entry *);
 int veriexec_rawchk(struct vnode *vp);
-
+int veriexec_block_verify(struct vnode *vp, struct vm_page **pps,
+			  voff_t offset, int *npages);
 #endif /* _KERNEL */
 
 #endif /* !_SYS_VERIFIED_EXEC_H_ */
Index: uvm/uvm_vnode.c
===================================================================
RCS file: /cvsroot/src/sys/uvm/uvm_vnode.c,v
retrieving revision 1.72
diff -u -r1.72 uvm_vnode.c
--- uvm/uvm_vnode.c	22 Jul 2006 08:47:56 -0000	1.72
+++ uvm/uvm_vnode.c	10 Sep 2006 11:18:03 -0000
@@ -56,6 +56,7 @@
 #include "opt_uvmhist.h"
 #include "opt_readahead.h"
 #include "opt_ddb.h"
+#include "veriexec.h"
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -69,6 +70,9 @@
 #include <sys/conf.h>
 #include <sys/pool.h>
 #include <sys/mount.h>
+#if NVERIEXEC > 0
+#include <sys/verified_exec.h>
+#endif
 
 #include <miscfs/specfs/specdev.h>
 
@@ -315,6 +319,11 @@
 	error = VOP_GETPAGES(vp, offset, pps, npagesp, centeridx,
 			     access_type, advice, flags);
 
+#if NVERIEXEC > 0
+	if (*npagesp > 0)
+		error = veriexec_block_verify(vp, pps, offset, npagesp);
+#endif /* NVERIEXEC > 0 */
+
 #if defined(READAHEAD_STATS)
 	if (((flags & PGO_LOCKED) != 0 && *npagesp > 0) ||
 	    ((flags & (PGO_LOCKED|PGO_SYNCIO)) == PGO_SYNCIO && error == 0)) {

--x+6KMIRAuhnl3hBn--