Skip to contentSkip to conversation

Crash in 2.4.17/ptrace

PATCH? Daniel Jacobowitz 6 participants 15 messages Jan 28, 2002 – Jan 30, 2002

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

I've been debugging frame buffer graphics lately, and encountering a very annoying problem. If the debugee has /dev/fb/0 mapped, and I try to print out the contents of a pointer into that buffer, GDB crashes in kernel/ptrace.c:access_process_vm. The problem seems to be that get_user_pages returns a NULL page. Something as simple as this prevents the crash:

kernel-source-2.4.17/kernel/ptrace.c renamed from 2.4.18-pre7/kernel/ptrace.c +12 -0
@@ -160,6 +160,18 @@ int access_process_vm(struct task_struct
 
 		flush_cache_page(vma, addr);
 
+#if 1
+		if (!page)
+		{
+			/* FIXME: Writes? */
+			if (!write) memset (buf, 0, bytes);
+			len -= bytes;
+			buf += bytes;
+			continue;
+		}
+#endif
+
+
 		maddr = kmap(page);
 		if (write) {
 			memcpy(maddr + offset, buf, bytes);

Of course, I would much rather be able to see the contents of the framebuffer. Any suggestions?

Signature · 2 lines
Daniel Jacobowitz                           Carnegie Mellon University
MontaVista Software                         Debian GNU/Linux Developer
#1 lore raw
Quoted 19 lines · Daniel Jacobowitz

I've been debugging frame buffer graphics lately, and encountering a very annoying problem. If the debugee has /dev/fb/0 mapped, and I try to print out the contents of a pointer into that buffer, GDB crashes in kernel/ptrace.c:access_process_vm. The problem seems to be that get_user_pages returns a NULL page. Something as simple as this prevents the crash:

kernel-source-2.4.17/kernel/ptrace.c Mon Jan 28 15:30:39 2002 renamed from 2.4.18-pre7/kernel/ptrace.c Fri Dec 21 12:42:04 2001 +5 -0
@@ -160,6 +160,18 @@ int access_process_vm(struct task_struct
 
                flush_cache_page(vma, addr);
 
+#if 1
+               if (!page)
+               {
+                       /* FIXME: Writes? */
+                       if (!write) memset (buf, 0, bytes);

kernel-source-2.4.17/kernel/ptrace.c Mon Jan 28 15:30:39 2002 int access_process_vm(struct task_struct · line 177

Quoted 10 lines
kernel-source-2.4.17/kernel/ptrace.c Mon Jan 28 15:30:39 2002 renamed from 2.4.18-pre7/kernel/ptrace.c Fri Dec 21 12:42:04 2001 +7 -0
+                       len -= bytes;
+                       buf += bytes;
+                       continue;
+               }
+#endif
+
+
                maddr = kmap(page);
                if (write) {
                        memcpy(maddr + offset, buf, bytes);

Oh nice. And it seems that, say, an O_DIRECT write of, say, a mmaped framebuffer will also oops the kernel.

Most callers of get_user_pages() aren't prepared for a null page* in the returned array.

This patch *may* be sufficient, but perhaps get_user_pages() should just bale out as soon as it finds an invalid page, rather than sticking a null page * into the returned array and continuing.

mm/memory.c +1 -0
@@ -453,6 +453,7 @@ int get_user_pages(struct task_struct *t
 		vma = find_extend_vma(mm, start);
 
 		if ( !vma ||
+		     (vma->vm_flags & VM_IO) ||
 		    (!force &&
 		     	((write && (!(vma->vm_flags & VM_WRITE))) ||
 		    	 (!write && (!(vma->vm_flags & VM_READ))) ) )) {
Quoted 2 lines

Of course, I would much rather be able to see the contents of the framebuffer. Any suggestions?

Not with this patch, I'm afraid. For your testing purposes you could just remove the VALID_PAGE() test in mm/memory.c:get_page_map(), and then gdb should be able to get at the framebuffer.

-

#2 lore raw
In reply to Daniel Jacobowitz
Quoted 13 lines · Andrew Morton

Oh nice. And it seems that, say, an O_DIRECT write of, say, a mmaped framebuffer will also oops the kernel.

Most callers of get_user_pages() aren't prepared for a null page* in the returned array.

This patch *may* be sufficient, but perhaps get_user_pages() should just bale out as soon as it finds an invalid page, rather than sticking a null page * into the returned array and continuing.

mm/memory.c int get_user_pages(struct task_struct *t · line 459

Quoted 7 lines
mm/memory.c +1 -0
@@ -453,6 +453,7 @@ int get_user_pages(struct task_struct *t
 		vma = find_extend_vma(mm, start);
 
 		if ( !vma ||
+		     (vma->vm_flags & VM_IO) ||
 		    (!force &&
 		     	((write && (!(vma->vm_flags & VM_WRITE))) ||
 		    	 (!write && (!(vma->vm_flags & VM_READ))) ) )) {

Frame buffers aren't reliable marked VM_IO when mapped, currently. Ben H. said he was going to push a fix for this at least to the PPC trees today or tomorrow.

It's cute - fbmem.c goes out of its way to set the flag on some architectures and not others. I can't imagine why.

But with that, yes, that should fix it.

Quoted 6 lines
Quoted 2 lines

Of course, I would much rather be able to see the contents of the framebuffer. Any suggestions?

Not with this patch, I'm afraid. For your testing purposes you could just remove the VALID_PAGE() test in mm/memory.c:get_page_map(), and then gdb should be able to get at the framebuffer.

I'm sure there's a good reason to not do that in general. Mind enlightening me?

Signature · 2 lines
Daniel Jacobowitz                           Carnegie Mellon University
MontaVista Software                         Debian GNU/Linux Developer
#3 lore raw
In reply to Daniel Jacobowitz
Quoted 4 lines · Daniel Jacobowitz

Frame buffers aren't reliable marked VM_IO when mapped, currently. Ben H. said he was going to push a fix for this at least to the PPC trees today or tomorrow.

They are now, I hope. I fixed that in 2.4.18-pre2.
drivers/video/fbmem.c:fb_mmap() marks the vma as
VM_IO for all architectures. But perhaps I missed some; an audit is needed in there, which I'll do.

Quoted 8 lines

It's cute - fbmem.c goes out of its way to set the flag on some architectures and not others. I can't imagine why.

But with that, yes, that should fix it.

Quoted 3 lines
Quoted 2 lines

Of course, I would much rather be able to see the contents of the framebuffer. Any suggestions?

Quoted 6 lines
Quoted 3 lines

Not with this patch, I'm afraid. For your testing purposes you could just remove the VALID_PAGE() test in mm/memory.c:get_page_map(), and then gdb should be able to get at the framebuffer.

I'm sure there's a good reason to not do that in general. Mind enlightening me?

Well, get_user_pages is used by several parts of the kernel. In the O_DIRECT/map_user_kiobuf case, we could end up asking the disk controller to perform busmastering against the video PCI device, which will probably explode somewhere down the chain.

Also, just because the hardware is mapped into the process virtual address space, it's not necessarily all accessible. It is possible to get a bus fault against part of the mapping. And the kernel doesn't expect to get bus faults on the source of copy_to_user, I think.

I'm sure Andrea will have a better notion than I. Sometimes I just fling out random patches to get people thinking about things ;)

-

#4 lore raw
Quoted 3 lines

In the O_DIRECT/map_user_kiobuf case, we could end up asking the disk controller to perform busmastering against the video PCI device, which will probably explode somewhere down the chain.

Actually we already know if this will fail or not. Check the pci quirks file We set a bunch of flags for safety issues on pci<->pci transfers. I did that anticipating one day more than just a few capture cards would care

Alan

#5 lore raw
In reply to Andrew Morton
Quoted 4 lines

Well, get_user_pages is used by several parts of the kernel. In the O_DIRECT/map_user_kiobuf case, we could end up asking the disk controller to perform busmastering against the video PCI device, which will probably explode somewhere down the chain.

Well... not sure. I'd like this to be doable. I have worked on some high-end broadcast video stuffs in the past, and we did intensive use of direct bus master from the disk controller to the framebuffer linear aperture. Actually, we even controlled the scatter gather list to "sort" lines ;)

If the HW cause a fault, the disk controller should stop and report an error, and the process should be signaled instead of getting an oops, but I don't know these code path in linux at all so...

Quoted 5 lines

Also, just because the hardware is mapped into the process virtual address space, it's not necessarily all accessible. It is possible to get a bus fault against part of the mapping. And the kernel doesn't expect to get bus faults on the source of copy_to_user, I think.

Well. My point of view here is fix copy_to_user, but well...

Quoted 3 lines

I'm sure Andrea will have a better notion than I. Sometimes I just fling out random patches to get people thinking about things ;)

Ben.

#6 lore raw
In reply to Andrew Morton
Quoted 9 lines · Benjamin Herrenschmidt
Quoted 4 lines

Well, get_user_pages is used by several parts of the kernel. In the O_DIRECT/map_user_kiobuf case, we could end up asking the disk controller to perform busmastering against the video PCI device, which will probably explode somewhere down the chain.

Well... not sure. I'd like this to be doable. I have worked on some high-end broadcast video stuffs in the past, and we did intensive use of direct bus master from the disk controller to the framebuffer linear aperture. Actually, we even controlled

you can do direct DMA to framebuffer on most sane hardware, yes. But the kiobuf structure on 2.4 doesn't allow that, it only works with valid 'page structures' not physical addresses. This is valid for both rawio and O_DIRECT. The MM part is the same for both of course.

Andrea

#7 lore raw
In reply to Daniel Jacobowitz
Quoted 5 lines · Andrew Morton

For your testing purposes you
could just remove the VALID_PAGE() test in mm/memory.c:get_page_map(), and then gdb should be able to get at the framebuffer.

Sorry, this won't work. We'll just get a random page struct outside mem_map[] and the kernel will die.

#8 lore raw
In reply to Daniel Jacobowitz
Quoted 11 lines · Andrew Morton
Quoted 4 lines · Daniel Jacobowitz

Frame buffers aren't reliable marked VM_IO when mapped, currently. Ben H. said he was going to push a fix for this at least to the PPC trees today or tomorrow.

They are now, I hope. I fixed that in 2.4.18-pre2.
drivers/video/fbmem.c:fb_mmap() marks the vma as
VM_IO for all architectures. But perhaps I missed some; an audit is needed in there, which I'll do.

I lied. Linus applied it, but not, it seems, Marcelo.

So. Here's a patch.

It marks all framebuffer mappings as VM_IO. This prevents kernel deadlocks which can occur when a program which has a framebuffer mapping attempts to dump core.

It also allows get_user_pages() to detect and skip these IO mappings, so ptrace, O_DIRECT, etc will not permit I/O against these mappings.

I've Cc'ed linux-fbdev-devel. Could someone please review?

drivers/video/acornfb.c +0 -3
@@ -1139,9 +1139,6 @@ acornfb_mmap(struct fb_info *info, struc
 	off += start;
 	vma->vm_pgoff = off >> PAGE_SHIFT;
 
-	/* This is an IO map - tell maydump to skip this VMA */
-	vma->vm_flags |= VM_IO;
-
 #ifdef CONFIG_CPU_32
 	pgprot_val(vma->vm_page_prot) &= ~L_PTE_CACHEABLE;
 #endif
drivers/video/igafb.c +0 -2
@@ -293,8 +293,6 @@ static int igafb_mmap(struct fb_info *in
 	if (!map_size)
 		return -EINVAL;
 
-	vma->vm_flags |= VM_IO;
-
 	if (!fb->mmaped) {
 		int lastconsole = 0;
 
drivers/video/sgivwfb.c +0 -1
@@ -846,7 +846,6 @@ static int sgivwfb_mmap(struct fb_info *
     return -EINVAL;
   offset += sgivwfb_mem_phys;
   pgprot_val(vma->vm_page_prot) = pgprot_val(vma->vm_page_prot) | _PAGE_PCD;
-  vma->vm_flags |= VM_IO;
   if (remap_page_range(vma->vm_start, offset, size, vma->vm_page_prot))
     return -EAGAIN;
   vma->vm_file = file;
drivers/video/fbmem.c +4 -3
@@ -543,6 +543,8 @@ fb_mmap(struct file *file, struct vm_are
 		lock_kernel();
 		res = fb->fb_mmap(info, file, vma);
 		unlock_kernel();
+		/* This is an IO map - tell maydump to skip this VMA */
+		vma->vm_flags |= VM_IO;
 		return res;
 	}
 
@@ -576,12 +578,13 @@ fb_mmap(struct file *file, struct vm_are
 		return -EINVAL;
 	off += start;
 	vma->vm_pgoff = off >> PAGE_SHIFT;
+	/* This is an IO map - tell maydump to skip this VMA */
+	vma->vm_flags |= VM_IO;
 #if defined(__sparc_v9__)
 	vma->vm_flags |= (VM_SHM | VM_LOCKED);
 	if (io_remap_page_range(vma->vm_start, off,
 				vma->vm_end - vma->vm_start, vma->vm_page_prot, 0))
 		return -EAGAIN;
-	vma->vm_flags |= VM_IO;
 #else
 #if defined(__mc68000__)
 #if defined(CONFIG_SUN3)
@@ -607,8 +610,6 @@ fb_mmap(struct file *file, struct vm_are
 	pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED;
 #elif defined(__arm__)
 	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
-	/* This is an IO map - tell maydump to skip this VMA */
-	vma->vm_flags |= VM_IO;
 #elif defined(__sh__)
 	pgprot_val(vma->vm_page_prot) &= ~_PAGE_CACHABLE;
 #else
#9 lore raw
Quoted 4 lines

It marks all framebuffer mappings as VM_IO. This prevents kernel deadlocks which can occur when a program which has a framebuffer mapping attempts to dump core.

Good this is needed.

Quoted 4 lines

I've Cc'ed linux-fbdev-devel. Could someone please review?

--- linux-2.4.18-pre7/drivers/video/acornfb.c Thu Nov 22 23:02:58 2001

You missed the atyfb driver. Other then that it looks fine. Geert what do you think?

#10 lore raw
In reply to Andrew Morton
Quoted 7 lines · James Simmons
Quoted 4 lines

It marks all framebuffer mappings as VM_IO. This prevents kernel deadlocks which can occur when a program which has a framebuffer mapping attempts to dump core.

Good this is needed.

Quoted 7 lines
Quoted 4 lines

I've Cc'ed linux-fbdev-devel. Could someone please review?

--- linux-2.4.18-pre7/drivers/video/acornfb.c Thu Nov 22 23:02:58 2001

You missed the atyfb driver. Other then that it looks fine. Geert what do you think?

Ah, thanks. Also I missed sbusfb.c (not that it was fatal - the driver got it right anyway).

Here's the current patch. After some discussion with Ben Herrenschmidt, it now sets VM_IO against the vma *before* calling the subdriver's mmap method, just in case the driver really does want to clear that flag (presumably, a shadow buffer in main memory).

drivers/video/acornfb.c +0 -3
@@ -1139,9 +1139,6 @@ acornfb_mmap(struct fb_info *info, struc
 	off += start;
 	vma->vm_pgoff = off >> PAGE_SHIFT;
 
-	/* This is an IO map - tell maydump to skip this VMA */
-	vma->vm_flags |= VM_IO;
-
 #ifdef CONFIG_CPU_32
 	pgprot_val(vma->vm_page_prot) &= ~L_PTE_CACHEABLE;
 #endif
drivers/video/igafb.c +0 -2
@@ -293,8 +293,6 @@ static int igafb_mmap(struct fb_info *in
 	if (!map_size)
 		return -EINVAL;
 
-	vma->vm_flags |= VM_IO;
-
 	if (!fb->mmaped) {
 		int lastconsole = 0;
 
drivers/video/sgivwfb.c +0 -1
@@ -846,7 +846,6 @@ static int sgivwfb_mmap(struct fb_info *
     return -EINVAL;
   offset += sgivwfb_mem_phys;
   pgprot_val(vma->vm_page_prot) = pgprot_val(vma->vm_page_prot) | _PAGE_PCD;
-  vma->vm_flags |= VM_IO;
   if (remap_page_range(vma->vm_start, offset, size, vma->vm_page_prot))
     return -EAGAIN;
   vma->vm_file = file;
drivers/video/fbmem.c +12 -3
@@ -541,6 +541,16 @@ fb_mmap(struct file *file, struct vm_are
 	if (fb->fb_mmap) {
 		int res;
 		lock_kernel();
+		/*
+		 * This is an IO map - tell maydump to skip this VMA.
+		 * If, for some reason, the sub-driver wishes to allow the
+		 * mapping to be dumpable and accessible via ptrace then
+		 * it may clear VM_IO later.
+		 * (This only makes sense if the buffer is actually
+		 * in main memory, and is described by a page struct in
+		 * mem_map[])
+		 */
+		vma->vm_flags |= VM_IO;
 		res = fb->fb_mmap(info, file, vma);
 		unlock_kernel();
 		return res;
@@ -576,12 +586,13 @@ fb_mmap(struct file *file, struct vm_are
 		return -EINVAL;
 	off += start;
 	vma->vm_pgoff = off >> PAGE_SHIFT;
+	/* This is an IO map - tell maydump to skip this VMA */
+	vma->vm_flags |= VM_IO;
 #if defined(__sparc_v9__)
 	vma->vm_flags |= (VM_SHM | VM_LOCKED);
 	if (io_remap_page_range(vma->vm_start, off,
 				vma->vm_end - vma->vm_start, vma->vm_page_prot, 0))
 		return -EAGAIN;
-	vma->vm_flags |= VM_IO;
 #else
 #if defined(__mc68000__)
 #if defined(CONFIG_SUN3)
@@ -607,8 +618,6 @@ fb_mmap(struct file *file, struct vm_are
 	pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED;
 #elif defined(__arm__)
 	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
-	/* This is an IO map - tell maydump to skip this VMA */
-	vma->vm_flags |= VM_IO;
 #elif defined(__sh__)
 	pgprot_val(vma->vm_page_prot) &= ~_PAGE_CACHABLE;
 #else
drivers/video/sbusfb.c +0 -1
@@ -220,7 +220,6 @@ static int sbusfb_mmap(struct fb_info *i
 		page += map_size;
 	}
 	
-	vma->vm_flags |= VM_IO;
 	if (!fb->mmaped) {
 		int lastconsole = 0;
 		
drivers/video/aty/atyfb_base.c +0 -2
@@ -1426,8 +1426,6 @@ static int atyfb_mmap(struct fb_info *in
 	if (!map_size)
 		return -EINVAL;
 
-	vma->vm_flags |= VM_IO;
-
 	if (!fb->mmaped) {
 		int lastconsole = 0;
#11 lore raw
Quoted 3 lines

Ah, thanks. Also I missed sbusfb.c (not that it was fatal - the driver got it right anyway).

Great.

Quoted 5 lines

Here's the current patch. After some discussion with Ben Herrenschmidt, it now sets VM_IO against the vma *before* calling the subdriver's mmap method, just in case the driver really does want to clear that flag (presumably, a shadow buffer in main memory).

I also thought about it as well. Never thought about the issue with shadow buffers. Is it safe to move it before if (fb->fb_mmap)?

Quoted 19 lines
drivers/video/fbmem.c +10 -0
@@ -541,6 +541,16 @@ fb_mmap(struct file *file, struct vm_are
 	if (fb->fb_mmap) {
 		int res;
 		lock_kernel();
+		/*
+		 * This is an IO map - tell maydump to skip this VMA.
+		 * If, for some reason, the sub-driver wishes to allow the
+		 * mapping to be dumpable and accessible via ptrace then
+		 * it may clear VM_IO later.
+		 * (This only makes sense if the buffer is actually
+		 * in main memory, and is described by a page struct in
+		 * mem_map[])
+		 */
+		vma->vm_flags |= VM_IO;
 		res = fb->fb_mmap(info, file, vma);
 		unlock_kernel();
 		return res;
#12 lore raw
In reply to Daniel Jacobowitz
Quoted 16 lines · Daniel Jacobowitz
Quoted 15 lines · Andrew Morton

Oh nice. And it seems that, say, an O_DIRECT write of, say, a mmaped framebuffer will also oops the kernel.

Most callers of get_user_pages() aren't prepared for a null page* in the returned array.

This patch *may* be sufficient, but perhaps get_user_pages() should just bale out as soon as it finds an invalid page, rather than sticking a null page * into the returned array and continuing.

mm/memory.c +0 -0
@@ -453,6 +453,7 @@ int get_user_pages(struct task_struct *t
 		vma = find_extend_vma(mm, start);
 
Quoted 7 lines

mm/memory.c int get_user_pages(struct task_struct *t · line 459

Quoted 5 lines
mm/memory.c +1 -0
 		if ( !vma ||
+		     (vma->vm_flags & VM_IO) ||
 		    (!force &&
 		     	((write && (!(vma->vm_flags & VM_WRITE))) ||
 		    	 (!write && (!(vma->vm_flags & VM_READ))) ) )) {

Frame buffers aren't reliable marked VM_IO when mapped, currently. Ben

For this reason (and also because there aren't only framebuffers mmapped out there) I guess it's better to just add (yet another) flag to get_user_pages, so that it fails with an error when it encounters a page out of the mem_map array.

Quoted 11 lines

H. said he was going to push a fix for this at least to the PPC trees today or tomorrow.

It's cute - fbmem.c goes out of its way to set the flag on some architectures and not others. I can't imagine why.

But with that, yes, that should fix it.

Quoted 3 lines
Quoted 2 lines

Of course, I would much rather be able to see the contents of the framebuffer. Any suggestions?

Quoted 10 lines
Quoted 3 lines

Not with this patch, I'm afraid. For your testing purposes you could just remove the VALID_PAGE() test in mm/memory.c:get_page_map(), and then gdb should be able to get at the framebuffer.

I'm sure there's a good reason to not do that in general. Mind enlightening me?

Signature · 2 lines
Daniel Jacobowitz                           Carnegie Mellon University
MontaVista Software                         Debian GNU/Linux Developer

Andrea

#13 lore raw
In reply to Daniel Jacobowitz
Quoted 31 lines · Andrew Morton
Quoted 4 lines · Daniel Jacobowitz

Frame buffers aren't reliable marked VM_IO when mapped, currently. Ben H. said he was going to push a fix for this at least to the PPC trees today or tomorrow.

They are now, I hope. I fixed that in 2.4.18-pre2.
drivers/video/fbmem.c:fb_mmap() marks the vma as
VM_IO for all architectures. But perhaps I missed some; an audit is needed in there, which I'll do.

Quoted 8 lines

It's cute - fbmem.c goes out of its way to set the flag on some architectures and not others. I can't imagine why.

But with that, yes, that should fix it.

Quoted 3 lines
Quoted 2 lines

Of course, I would much rather be able to see the contents of the framebuffer. Any suggestions?

Quoted 6 lines
Quoted 3 lines

Not with this patch, I'm afraid. For your testing purposes you could just remove the VALID_PAGE() test in mm/memory.c:get_page_map(), and then gdb should be able to get at the framebuffer.

I'm sure there's a good reason to not do that in general. Mind enlightening me?

Well, get_user_pages is used by several parts of the kernel. In the O_DIRECT/map_user_kiobuf case, we could end up asking the disk controller to perform busmastering against the video PCI device, which will probably explode somewhere down the chain.

Quoted 5 lines

Also, just because the hardware is mapped into the process virtual address space, it's not necessarily all accessible. It is possible to get a bus fault against part of the mapping. And the kernel doesn't expect to get bus faults on the source of copy_to_user, I think.

another basic problem about allowing map_user_kiobuf to succeed on invalid pages, is that calling PageReserved/SetPageDirty on a null pointer will crash, etc...

Quoted 4 lines

I'm sure Andrea will have a better notion than I. Sometimes I just fling out random patches to get people thinking about things ;)

Well, I think your earlier suggestion to bale out with an error if an invalid page is found sounds like the cleaner fix (possibly in function of yet another bitflag, so if somebody wants to get the nearby pages regardless of an invalid pages somewhere, it can).

Andrea

#14 lore raw
In reply to Daniel Jacobowitz
Quoted 7 lines · Andrea Arcangeli

...
Well, I think your earlier suggestion to bale out with an error if an invalid page is found sounds like the cleaner fix (possibly in function of yet another bitflag, so if somebody wants to get the nearby pages regardless of an invalid pages somewhere, it can).

I find it rather hard to decide about this. get_user_pages() leaves null page pointers in the page[] array for invalid pages, and that's a reasonable API, as long as all callers are actually aware of it....

In the O_DIRECT case, the kernel does not crash, because brw_kiovec() does:

                        map  = iobuf->maplist[pageind];
                        if (!map) {
                                err = -EFAULT;
                                goto finished;
                        }

However, I think it _would_ crash if the first entry in the maplist[] was non-null, and the second is null, because that would cause generic_file_direct_IO() to call mark_dirty_kiobuf(), and mark_dirty_kiobuf() forgets to check for NULL page *'s in the maplist[].

Given the difficulty of testing all this, and the dubious benefit in allowing a holey maplist[], I'm inclined to just disallow it
in 2.4. What do you think?

mm/memory.c +16 -2
@@ -453,6 +453,7 @@ int get_user_pages(struct task_struct *t
 		vma = find_extend_vma(mm, start);
 
 		if ( !vma ||
+		     (vma->vm_flags & VM_IO) ||
 		    (!force &&
 		     	((write && (!(vma->vm_flags & VM_WRITE))) ||
 		    	 (!write && (!(vma->vm_flags & VM_READ))) ) )) {
@@ -486,8 +487,9 @@ int get_user_pages(struct task_struct *t
 				/* FIXME: call the correct function,
 				 * depending on the type of the found page
 				 */
-				if (pages[i])
-					page_cache_get(pages[i]);
+				if (!pages[i])
+					goto bad_page;
+				page_cache_get(pages[i]);
 			}
 			if (vmas)
 				vmas[i] = vma;
@@ -497,7 +499,19 @@ int get_user_pages(struct task_struct *t
 		} while(len && start < vma->vm_end);
 		spin_unlock(&mm->page_table_lock);
 	} while(len);
+out:
 	return i;
+
+	/*
+	 * We found an invalid page in the VMA.  Release all we have
+	 * so far and fail.
+	 */
+bad_page:
+	spin_unlock(&mm->page_table_lock);
+	while (i--)
+		page_cache_release(pages[i]);
+	i = -EFAULT;
+	goto out;
 }
 
 /*