PATCH: "gdb"" breakpoints (i.e., ptrace peeks/pokes) hang 2.1.37

Kevin Buhr (buhr@stat.wisc.edu)
15 May 1997 12:10:49 -0500


Linus:

Between pre-patch-2.1.37-7 and 2.1.37, external calls to "do_no_page"
and "do_wp_page" were changed to "handle_mm_fault" calls. However, in
some cases the original "do_no_page" and "do_wp_page" calls took a
"tsk" argument not equal to "current", and this fact is not
communicated to "handle_mm_fault".

In particular, as reported to the list by a few others, using "gdb"
breakpoints (e.g., "break main; run") hangs the 2.1.37 kernel, since
the "ptrace" peeks and pokes end up trying to do "handle_mm_fault"s on
behalf of the child process while the "current" process is the parent.

The enclosed patch against 2.1.37 defines a "handle_mm_fault_tsk" that
takes an explicit "tsk" argument, redefines "handle_mm_fault" as an
inline that calls "handle_mm_fault_tsk" with "tsk==current", and fixes
the places in the kernel where "do_xx_page" became "handle_mm_fault"
for "tsk!=current". (However, I only checked for changes between
2.1.pre37-7 and 2.1.37.)

A quick test shows that this fixes the hang-on-breakpoint bug
mentioned above.

Kevin <buhr@stat.wisc.edu>

* * *

Index: linux/arch/i386/kernel/ptrace.c
diff -u linux/arch/i386/kernel/ptrace.c:1.1.1.3 linux/arch/i386/kernel/ptrace.c:1.2
--- linux/arch/i386/kernel/ptrace.c:1.1.1.3 Wed May 14 10:00:41 1997
+++ linux/arch/i386/kernel/ptrace.c Thu May 15 12:32:11 1997
@@ -83,7 +83,7 @@
repeat:
pgdir = pgd_offset(vma->vm_mm, addr);
if (pgd_none(*pgdir)) {
- handle_mm_fault(vma, addr, 0);
+ handle_mm_fault_tsk(tsk, vma, addr, 0);
goto repeat;
}
if (pgd_bad(*pgdir)) {
@@ -93,7 +93,7 @@
}
pgmiddle = pmd_offset(pgdir, addr);
if (pmd_none(*pgmiddle)) {
- handle_mm_fault(vma, addr, 0);
+ handle_mm_fault_tsk(tsk, vma, addr, 0);
goto repeat;
}
if (pmd_bad(*pgmiddle)) {
@@ -103,7 +103,7 @@
}
pgtable = pte_offset(pgmiddle, addr);
if (!pte_present(*pgtable)) {
- handle_mm_fault(vma, addr, 0);
+ handle_mm_fault_tsk(tsk, vma, addr, 0);
goto repeat;
}
page = pte_page(*pgtable);
@@ -134,7 +134,7 @@
repeat:
pgdir = pgd_offset(vma->vm_mm, addr);
if (!pgd_present(*pgdir)) {
- handle_mm_fault(vma, addr, 1);
+ handle_mm_fault_tsk(tsk, vma, addr, 1);
goto repeat;
}
if (pgd_bad(*pgdir)) {
@@ -144,7 +144,7 @@
}
pgmiddle = pmd_offset(pgdir, addr);
if (pmd_none(*pgmiddle)) {
- handle_mm_fault(vma, addr, 1);
+ handle_mm_fault_tsk(tsk, vma, addr, 1);
goto repeat;
}
if (pmd_bad(*pgmiddle)) {
@@ -154,12 +154,12 @@
}
pgtable = pte_offset(pgmiddle, addr);
if (!pte_present(*pgtable)) {
- handle_mm_fault(vma, addr, 1);
+ handle_mm_fault_tsk(tsk, vma, addr, 1);
goto repeat;
}
page = pte_page(*pgtable);
if (!pte_write(*pgtable)) {
- handle_mm_fault(vma, addr, 1);
+ handle_mm_fault_tsk(tsk, vma, addr, 1);
goto repeat;
}
/* this is a hack for non-kernel-mapped video buffers and similar */
Index: linux/fs/proc/mem.c
diff -u linux/fs/proc/mem.c:1.1.1.3 linux/fs/proc/mem.c:1.2
--- linux/fs/proc/mem.c:1.1.1.3 Wed May 14 09:58:07 1997
+++ linux/fs/proc/mem.c Thu May 15 12:32:14 1997
@@ -285,10 +285,10 @@
return -ENOMEM;

if (!pte_present(*src_table))
- handle_mm_fault(src_vma, stmp, 1);
+ handle_mm_fault_tsk(tsk, src_vma, stmp, 1);

if ((vma->vm_flags & VM_WRITE) && !pte_write(*src_table))
- handle_mm_fault(src_vma, stmp, 1);
+ handle_mm_fault_tsk(tsk, src_vma, stmp, 1);

set_pte(src_table, pte_mkdirty(*src_table));
set_pte(dest_table, *src_table);
Index: linux/include/linux/mm.h
diff -u linux/include/linux/mm.h:1.1.1.3 linux/include/linux/mm.h:1.2
--- linux/include/linux/mm.h:1.1.1.3 Wed May 14 09:58:21 1997
+++ linux/include/linux/mm.h Thu May 15 12:32:15 1997
@@ -260,7 +260,8 @@
extern int zeromap_page_range(unsigned long from, unsigned long size, pgprot_t prot);

extern void vmtruncate(struct inode * inode, unsigned long offset);
-extern void handle_mm_fault(struct vm_area_struct *vma, unsigned long address, int write_access);
+extern void handle_mm_fault_tsk(struct task_struct * tsk, struct vm_area_struct *vma,
+ unsigned long address, int write_access);

extern unsigned long paging_init(unsigned long start_mem, unsigned long end_mem);
extern void mem_init(unsigned long start_mem, unsigned long end_mem);
@@ -268,7 +269,13 @@
extern void oom(struct task_struct * tsk);
extern void si_meminfo(struct sysinfo * val);

+extern inline void handle_mm_fault(struct vm_area_struct * vma, unsigned long address,
+ int write_access)
+{
+ handle_mm_fault_tsk(current, vma, address, write_access);
+}
+
/* mmap.c */
extern void vma_init(void);
extern unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags, unsigned long off);
Index: linux/mm/memory.c
diff -u linux/mm/memory.c:1.1.1.2 linux/mm/memory.c:1.2
--- linux/mm/memory.c:1.1.1.2 Wed May 14 09:58:20 1997
+++ linux/mm/memory.c Thu May 15 12:32:16 1997
@@ -885,11 +885,11 @@
* with external mmu caches can use to update those (ie the Sparc or
* PowerPC hashed page tables that act as extended TLBs).
*/
-static inline void handle_pte_fault(struct vm_area_struct * vma, unsigned long address,
- int write_access, pte_t * pte)
+static inline void handle_pte_fault(struct task_struct * tsk, struct vm_area_struct * vma,
+ unsigned long address, int write_access, pte_t * pte)
{
if (!pte_present(*pte)) {
- do_no_page(current, vma, address, write_access);
+ do_no_page(tsk, vma, address, write_access);
return;
}
set_pte(pte, pte_mkyoung(*pte));
@@ -901,11 +901,11 @@
flush_tlb_page(vma, address);
return;
}
- do_wp_page(current, vma, address, write_access);
+ do_wp_page(tsk, vma, address, write_access);
}

-void handle_mm_fault(struct vm_area_struct * vma, unsigned long address,
- int write_access)
+void handle_mm_fault_tsk(struct task_struct * tsk, struct vm_area_struct * vma,
+ unsigned long address, int write_access)
{
pgd_t *pgd;
pmd_t *pmd;
@@ -918,9 +918,9 @@
pte = pte_alloc(pmd, address);
if (!pte)
goto no_memory;
- handle_pte_fault(vma, address, write_access, pte);
+ handle_pte_fault(tsk, vma, address, write_access, pte);
update_mmu_cache(vma, address, *pte);
return;
no_memory:
- oom(current);
+ oom(tsk);
}