diff -ur linux-2.4.21/drivers/audit/args.c audit/drivers/audit/args.c
--- linux-2.4.21/drivers/audit/args.c	2003-10-29 19:11:23.000000000 +0100
+++ audit/drivers/audit/args.c	2003-10-29 19:09:48.000000000 +0100
@@ -63,30 +63,112 @@
 #endif
 
 /*
+ * Initialize VM info
+ */
+void
+audit_init_vm(struct aud_process *pinfo)
+{
+	struct mm_struct *mm = current->mm;
+	struct aud_vm_info *vmi;
+
+	DPRINTF("called.\n");
+	BUG_ON(pinfo->vm_info);
+
+	if (mm == NULL)
+		return;
+
+	/* Allocate a VM info struct that is shared by
+	 * all threads */
+	do {
+		vmi = kmalloc(sizeof(*vmi), GFP_KERNEL);
+		if (vmi)
+			break;
+		schedule_timeout(HZ);
+	} while (1);
+
+	memset(vmi, 0, sizeof(*vmi));
+	init_rwsem(&vmi->lock);
+	atomic_set(&vmi->refcnt, 1);
+
+	vmi->mm = current->mm;
+	atomic_inc(&current->mm->mm_users);
+
+	pinfo->vm_info = vmi;
+}
+
+void
+audit_release_vm(struct aud_process *pinfo)
+{
+	struct aud_vm_info *vmi;
+
+	if ((vmi = pinfo->vm_info) == NULL)
+		return;
+
+	DPRINTF("called, refcnt=%d.\n", atomic_read(&vmi->refcnt));
+
+	/* Release scratch pages we mapped previously */
+	audit_release_scratch_vm(pinfo);
+
+	pinfo->vm_info = NULL;
+	if (atomic_dec_and_test(&vmi->refcnt)) {
+		mmput(vmi->mm);
+		kfree(vmi);
+	}
+}
+
+void
+audit_copy_vm(struct aud_process *pinfo, struct aud_process *parent)
+{
+	struct aud_vm_info *vmi = parent->vm_info;
+
+	pinfo->vm_info = vmi;
+	if (vmi)
+		atomic_inc(&vmi->refcnt);
+}
+
+/*
  * Lock/unlock VM semaphore
  */
 static inline void
-audit_lock_vm(struct aud_process *pinfo)
+audit_lock_vm(struct aud_process *pinfo, int how)
 {
-	struct mm_struct *mm = current->mm;
+	struct aud_vm_info *vmi;
+
+	if (!pinfo->flags & how)
+		return;
 
-	if (!pinfo->locked_mm) {
-		atomic_inc(&mm->mm_users);
-		down_read(&mm->mmap_sem);
-		pinfo->locked_mm = mm;
+	if (how == AUD_F_VM_LOCKED_R) {
+		if (pinfo->flags & AUD_F_VM_LOCKED_W)
+			return;
+	} else {
+		if (pinfo->flags & AUD_F_VM_LOCKED_R)
+			BUG();
 	}
+
+	if (!(vmi = pinfo->vm_info))
+		BUG();
+
+	if (how == AUD_F_VM_LOCKED_R)
+		down_read(&vmi->lock);
+	else
+		down_write(&vmi->lock);
+
+	pinfo->flags |= how;
 }
 
 static inline void
 audit_unlock_vm(struct aud_process *pinfo)
 {
-	struct mm_struct *mm;
+	struct aud_vm_info *vmi;
 
-	if ((mm = pinfo->locked_mm) != NULL) {
-		up_read(&mm->mmap_sem);
-		pinfo->locked_mm = NULL;
-		mmput(mm);
+	if ((vmi = pinfo->vm_info) != NULL) {
+		if (pinfo->flags & AUD_F_VM_LOCKED_R)
+			up_read(&vmi->lock);
+		else if (pinfo->flags & AUD_F_VM_LOCKED_W)
+			up_write(&vmi->lock);
 	}
+
+	pinfo->flags &= ~(AUD_F_VM_LOCKED_R|AUD_F_VM_LOCKED_W);
 }
 
 /*
@@ -104,7 +186,8 @@
 
 	DPRINTF("Allocating scratch info for process %d\n", current->pid);
 	BUG_ON(mm == NULL);
-	BUG_ON(pinfo->mm != NULL);
+	BUG_ON(pinfo->vm_info == NULL);
+	BUG_ON(pinfo->vm_info->mm != mm);
 
 	/* Make this constant for now */
 	size = audit_scratch_vm_size;
@@ -119,15 +202,12 @@
 	if (IS_ERR((void *) addr))
 		return addr;
 
-	pinfo->mm      = mm;
 	pinfo->vm_area = find_vma(mm, addr);
 	pinfo->vm_addr = addr;
 	pinfo->vm_size = size;
 	memset(pinfo->vm_page, 0, sizeof(pinfo->vm_page));
 	memset(pinfo->vm_phys, 0, sizeof(pinfo->vm_phys));
 
-	atomic_inc(&mm->mm_users);
-
 	return 0;
 }
 
@@ -137,16 +217,27 @@
 static int
 audit_validate_scratch_vm(struct aud_process *pinfo)
 {
+	struct aud_vm_info *vmi;
 	struct mm_struct *mm = current->mm;
 	struct vm_area_struct	*vma;
 
-	if (pinfo->vm_addr == 0)
+	if (!(vmi = pinfo->vm_info))
+		BUG();
+
+	if (vmi->mm != mm) {
+		/* We may have called execve(), so the mm struct
+		 * has changed. Drop the entire VM info struct now,
+		 * and allocate a fresh one */
+		audit_unlock_vm(pinfo);
+		audit_release_vm(pinfo);
+		audit_init_vm(pinfo);
 		return 0;
+	}
 
-	/* We may have called execve(), so the mm struct has changed.
-	 * We should drop it now. */
-	if (pinfo->mm != mm)
+	if (pinfo->vm_addr == 0) {
+		audit_unlock_vm(pinfo);
 		return 0;
+	}
 
 	vma = find_vma(mm, pinfo->vm_addr);
 	if (vma == NULL
@@ -154,8 +245,13 @@
 	 || pinfo->vm_addr + pinfo->vm_size > vma->vm_end
 	 || vma->vm_file
 	 || (vma->vm_flags & VM_WRITE)) {
+		/* The process messed with its VM in a way we cannot
+		 * tolerate. We would be justified to nuke it right here,
+		 * but we play nice and just re-map it */
 		printk(KERN_NOTICE "Process %u (login uid=%u) changed its scratch VM\n",
 				current->pid, pinfo->audit_uid);
+		audit_unlock_vm(pinfo);
+		audit_release_scratch_vm(pinfo);
 		return 0;
 	}
 
@@ -168,6 +264,7 @@
 static inline int
 audit_map_scratch_page(struct aud_process *pinfo, unsigned int nr)
 {
+	struct aud_vm_info *vmi = pinfo->vm_info;
 	struct vm_area_struct *vma;
 	unsigned long	addr;
 	struct page	*page;
@@ -176,9 +273,10 @@
 	if (nr >= AUDIT_MAX_SCRATCH_PAGES)
 		return -EINVAL;
 
+	BUG_ON(vmi == NULL);
 	if ((page = pinfo->vm_page[nr]) == NULL) {
 		addr = pinfo->vm_addr + nr * PAGE_SIZE;
-		err = get_user_pages(current, pinfo->mm, addr, 1, 1, 1, &page, &vma);
+		err = get_user_pages(current, vmi->mm, addr, 1, 1, 1, &page, &vma);
 		if (err < 0) {
 			printk(KERN_NOTICE "%s: get_user_pages failed, err=%d\n",
 					__FUNCTION__, err);
@@ -198,15 +296,17 @@
 void
 audit_release_scratch_vm(struct aud_process *pinfo)
 {
+	struct aud_vm_info *vmi;
 	struct mm_struct *mm;
 	unsigned int	i;
 
-	if ((mm = pinfo->mm) == NULL)
+	if (!(vmi = pinfo->vm_info))
 		return;
 
 	/* Release lock on scratch memory if we hold it */
 	audit_unlock_vm(pinfo);
 
+	mm = vmi->mm;
 	down_write(&mm->mmap_sem);
 
 	if (pinfo->vm_addr) {
@@ -230,8 +330,6 @@
 	}
 
 	up_write(&mm->mmap_sem);
-	pinfo->mm = NULL;
-	mmput(mm);
 }
 
 /*
@@ -240,6 +338,7 @@
 static int
 need_to_lock(unsigned long addr, size_t len)
 {
+	struct aud_vm_info *vmi = ((struct aud_process *) current->audit)->vm_info;
 	struct mm_struct *mm = current->mm;
 	struct vm_area_struct *vma;
 	struct file	*f;
@@ -284,9 +383,8 @@
 
 			res |= AUD_ARG_NEED_LOCK | AUD_ARG_NEED_COPY;
 		} else
-		if (atomic_read(&mm->mm_users) != 2) {
-			/* Two references are normal - one by the process,
-			 * and one by the audit subsystem */
+		if (atomic_read(&vmi->refcnt) != 1) {
+			/* entire VM shared with another process */
 			res |= AUD_ARG_NEED_LOCK;
 			if (vma->vm_flags & (VM_WRITE|VM_MAYWRITE))
 				res |= AUD_ARG_NEED_COPY;
@@ -825,7 +923,7 @@
 }
 
 int
-audit_lock_arguments(struct aud_syscall_data *sc)
+audit_lock_arguments(struct aud_syscall_data *sc, int how)
 {
 	struct aud_process	*pinfo;
 	struct sysarg_data	*target;
@@ -842,15 +940,12 @@
 
 	pinfo = (struct aud_process *) current->audit;
 
-	audit_lock_vm(pinfo);
-
+	audit_lock_vm(pinfo, how);
 	if (!audit_validate_scratch_vm(pinfo)) {
-		/* VM has changed. Release scratch and re-map it. */
-		audit_unlock_vm(pinfo);
-		audit_release_scratch_vm(pinfo);
+		/* VM has changed. Re-map and re-lock it. */
 		if ((err = audit_alloc_scratch_vm(pinfo)) < 0)
 			return err;
-		audit_lock_vm(pinfo);
+		audit_lock_vm(pinfo, how);
 	}
 
 	/* We can use all the scratch pages we have */
diff -ur linux-2.4.21/drivers/audit/audit-private.h audit/drivers/audit/audit-private.h
--- linux-2.4.21/drivers/audit/audit-private.h	2003-10-29 19:11:23.000000000 +0100
+++ audit/drivers/audit/audit-private.h	2003-10-29 19:09:48.000000000 +0100
@@ -177,6 +177,12 @@
  * for the duration of the system call.
  */
 #define AUDIT_MAX_SCRATCH_PAGES	2
+struct aud_vm_info {
+	atomic_t		refcnt;
+	struct mm_struct *	mm;
+	struct rw_semaphore	lock;
+};
+
 struct aud_process {
 	struct list_head	list;
 
@@ -184,8 +190,7 @@
 	unsigned int		audit_id;
 
 	/* scratch memory */
-	struct mm_struct *	mm;
-	struct mm_struct *	locked_mm;
+	struct aud_vm_info *	vm_info;
 	unsigned long		vm_addr;
 	size_t			vm_size,
 				vm_used;
@@ -202,7 +207,8 @@
 };
 
 #define AUD_F_SUSPENDED		0x0001
-#define AUD_F_VM_LOCKED		0x0002
+#define AUD_F_VM_LOCKED_R	0x0002
+#define AUD_F_VM_LOCKED_W	0x0004
 #define AUD_F_FS_LOCKED_R	0x0008
 #define AUD_F_FS_LOCKED_W	0x0010
 
@@ -279,8 +285,11 @@
 void			audit_filter_put(struct aud_filter *);
 int			audit_filter_clear(void);
 
+void			audit_init_vm(struct aud_process *);
+void			audit_copy_vm(struct aud_process *, struct aud_process *);
+void			audit_release_vm(struct aud_process *);
 void			audit_release_scratch_vm(struct aud_process *);
-int			audit_lock_arguments(struct aud_syscall_data *);
+int			audit_lock_arguments(struct aud_syscall_data *, int);
 
 struct aud_file_object *audit_fileset_add(char *);
 void			audit_fileset_release(struct aud_file_object *);
diff -ur linux-2.4.21/drivers/audit/control.c audit/drivers/audit/control.c
--- linux-2.4.21/drivers/audit/control.c	2003-10-29 19:11:23.000000000 +0100
+++ audit/drivers/audit/control.c	2003-10-29 19:12:08.000000000 +0100
@@ -65,7 +65,7 @@
 int				audit_allow_suspend = 1;
 int				audit_paranoia = 1;
 
-static int	__audit_attach(struct task_struct *, int);
+static int	__audit_attach(struct task_struct *, int, struct aud_process *);
 static void	audit_attach_all(void);
 static void	audit_detach_all(void);
 
@@ -453,7 +453,7 @@
 	if ((parent_info = parent->audit) == NULL)
 		return;
 
-	if (__audit_attach(child, 0) != 0) {
+	if (__audit_attach(child, 0, parent_info) != 0) {
 		printk(KERN_ERR "audit: failed to enable auditing for child process!\n");
 		return;
 	}
@@ -553,17 +553,21 @@
 		case __NR_mlock:
 		case __NR_mlockall:
 		case __NR_munlock:
-			/* These calls take the MM semaphore, so
-			 * we better not do the same */
+			/* These calls mess with the process VM.
+			 * Make sure no other thread sharing this VM is
+			 * doing any audited call at this time. */
+			error = audit_lock_arguments(sc, AUD_F_VM_LOCKED_W);
 			break;
 		case __NR_execve:
 			/* Same as above, except we need to preserve
 			 * arguments for posterity. */
-			error = audit_copy_arguments(sc);
+			error = audit_lock_arguments(sc, AUD_F_VM_LOCKED_W);
+			if (error >= 0)
+				error = audit_copy_arguments(sc);
 			break;
 		default:
 		lock_args:
-			error = audit_lock_arguments(sc);
+			error = audit_lock_arguments(sc, AUD_F_VM_LOCKED_R);
 			break;
 		}
 	} else {
@@ -772,7 +776,7 @@
  * Attach/detach audit context to process
  */
 static int
-__audit_attach(struct task_struct *task, int suspended)
+__audit_attach(struct task_struct *task, int suspended, struct aud_process *parent)
 {
 	struct aud_process *pinfo;
 	int		res = 0;
@@ -797,6 +801,11 @@
 		task->audit = pinfo;
 		task->ptrace |= PT_AUDITED;
 		task_unlock(task);
+
+		if (parent == NULL)
+			audit_init_vm(pinfo);
+		else
+			audit_copy_vm(pinfo, parent);
 	}
 	return res;
 }
@@ -813,7 +822,7 @@
 	if (!audit_message_enabled)
 		return -ENODEV;
 
-	return __audit_attach(current, suspended);
+	return __audit_attach(current, suspended, NULL);
 }
 
 static int
@@ -835,7 +844,7 @@
 		/* Free any memory we may have allocated for
 	   	 * argument data, and release VM scratch memory */
 		audit_release_arguments(pinfo);
-		audit_release_scratch_vm(pinfo);
+		audit_release_vm(pinfo);
 		kfree(pinfo);
 	}
 	return res;
@@ -862,7 +871,7 @@
 		 && p != current
 		 && p->mm != NULL
 		 && p->pid != 1)
-			__audit_attach(p, 0);
+			__audit_attach(p, 0, NULL);
 	}
 	read_unlock(&tasklist_lock);
 }
