CINXE.COM

LKML: Roland McGrath: [PATCH] x86_64: handle iret faults better

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>LKML: Roland McGrath: [PATCH] x86_64: handle iret faults better</title><link href="/css/message.css" rel="stylesheet" type="text/css" /><link href="/css/wrap.css" rel="alternate stylesheet" type="text/css" title="wrap" /><link href="/css/nowrap.css" rel="stylesheet" type="text/css" title="nowrap" /><link href="/favicon.ico" rel="shortcut icon" /><script src="/js/simple-calendar.js" type="text/javascript"></script><script src="/js/styleswitcher.js" type="text/javascript"></script><link rel="alternate" type="application/rss+xml" title="lkml.org : last 100 messages" href="/rss.php" /><link rel="alternate" type="application/rss+xml" title="lkml.org : last messages by Roland McGrath" href="/groupie.php?aid=8730" /><!--Matomo--><script> var _paq = window._paq = window._paq || []; /* tracker methods like "setCustomDimension" should be called before "trackPageView" */ _paq.push(["setDoNotTrack", true]); _paq.push(["disableCookies"]); _paq.push(['trackPageView']); _paq.push(['enableLinkTracking']); (function() { var u="//m.lkml.org/"; _paq.push(['setTrackerUrl', u+'matomo.php']); _paq.push(['setSiteId', '1']); var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s); })(); </script><!--End Matomo Code--></head><body onload="es.jasper.simpleCalendar.init();" itemscope="itemscope" itemtype="http://schema.org/BlogPosting"><table border="0" cellpadding="0" cellspacing="0"><tr><td width="180" align="center"><a href="/"><img style="border:0;width:135px;height:32px" src="/images/toprowlk.gif" alt="lkml.org" /></a></td><td width="32">聽</td><td class="nb"><div><a class="nb" href="/lkml"> [lkml]</a> 聽 <a class="nb" href="/lkml/2005"> [2005]</a> 聽 <a class="nb" href="/lkml/2005/4"> [Apr]</a> 聽 <a class="nb" href="/lkml/2005/4/23"> [23]</a> 聽 <a class="nb" href="/lkml/last100"> [last100]</a> 聽 <a href="/rss.php"><img src="/images/rss-or.gif" border="0" alt="RSS Feed" /></a></div><div>Views: <a href="#" class="nowrap" onclick="setActiveStyleSheet('wrap');return false;">[wrap]</a><a href="#" class="wrap" onclick="setActiveStyleSheet('nowrap');return false;">[no wrap]</a> 聽 <a class="nb" href="/lkml/mheaders/2005/4/23/129" onclick="this.href='/lkml/headers'+'/2005/4/23/129';">[headers]</a>聽 <a href="/lkml/bounce/2005/4/23/129">[forward]</a>聽 </div></td><td width="32">聽</td></tr><tr><td valign="top"><div class="es-jasper-simpleCalendar" baseurl="/lkml/"></div><div class="threadlist">Messages in this thread</div><ul class="threadlist"><li class="root"><a href="/lkml/2005/4/23/129">First message in thread</a></li><li class="origin"><a href="/lkml/2005/4/25/84">Roland McGrath</a><ul><li><a href="/lkml/2005/4/25/84">Andi Kleen</a><ul><li><a href="/lkml/2005/4/25/91">Linus Torvalds</a><ul><li><a href="/lkml/2005/4/25/236">Roland McGrath</a><ul><li><a href="/lkml/2005/4/25/242">Linus Torvalds</a></li></ul></li></ul></li></ul></li></ul></li></ul><div class="threadlist">Patch in this message</div><ul class="threadlist"><li><a href="/lkml/diff/2005/4/23/129/1">Get diff 1</a></li></ul></td><td width="32" rowspan="2" class="c" valign="top"><img src="/images/icornerl.gif" width="32" height="32" alt="/" /></td><td class="c" rowspan="2" valign="top" style="padding-top: 1em"><table><tr><td><table><tr><td class="lp">Date</td><td class="rp" itemprop="datePublished">Sat, 23 Apr 2005 17:50:37 -0700</td></tr><tr><td class="lp">From</td><td class="rp" itemprop="author">Roland McGrath &lt;&gt;</td></tr><tr><td class="lp">Subject</td><td class="rp" itemprop="name">[PATCH] x86_64: handle iret faults better</td></tr></table></td><td></td></tr></table><pre itemprop="articleBody">This is the x86_64 variant of the i386 fix I just submitted. I think<br />iret can only produce these faults when returning to user mode in a<br />32-bit process. The failure mode is even more mysterious on x86_64,<br />because it exits with -9999&amp;0x7f instead of 11 (SIGSEGV), so it says<br />"Unknown signal 113 (core dumped)" when it exits without actually<br />trying to dump a core file.<br /><br /><br />Thanks,<br />Roland<br /><br /><br />Signed-off-by: Roland McGrath &lt;roland&#64;redhat.com&gt;<br /><br />--- linux-2.6/arch/x86_64/kernel/entry.S<br />+++ linux-2.6/arch/x86_64/kernel/entry.S<br />&#64;&#64; -471,14 +470,6 &#64;&#64; iret_label: <br /> .section __ex_table,"a"<br /> .quad iret_label,bad_iret <br /> .previous<br />- .section .fixup,"ax"<br />- /* force a signal here? this matches i386 behaviour */<br />- /* running with kernel gs */<br />-bad_iret:<br />- movq $-9999,%rdi /* better code? */<br />- jmp do_exit <br />- .previous <br />- <br /> /* edi: workmask, edx: work */ <br /> retint_careful:<br /> bt $TIF_NEED_RESCHED,%edx<br />&#64;&#64; -522,6 +513,31 &#64;&#64; retint_kernel: <br /> #endif <br /> CFI_ENDPROC<br /> <br />+ /*<br />+ * Traps in iret mean that userland tried to restore a bogus<br />+ * cs, eip, ss, esp, or eflags. Some kinds of bogosity just cause<br />+ * a trap after the iret returns, but some will cause a trap in<br />+ * iret itself. We want to treat those as if the restored user<br />+ * state is what caused that trap, i.e. produce the appropriate signal.<br />+ * Since normal .fixup code doesn't have access to the trap info,<br />+ * traps.c has a special case for iret. It's already generated the<br />+ * signal before we resume at bad_iret. Now we just need to recover<br />+ * the whole frame we were trying to restore, so it can be seen on<br />+ * our stack by the debugger.<br />+ */<br />+ .section .fixup,"ax"<br />+ /* running with kernel gs */<br />+ENTRY(bad_iret)<br />+ CFI_STARTPROC simple<br />+ CFI_DEF_CFA rsp,(SS+8-RIP)<br />+ CFI_REL_OFFSET rip,0<br />+ CFI_REL_OFFSET rsp,(RSP-RIP)<br />+ SAVE_ARGS 8<br />+ jmp exit_intr<br />+ CFI_ENDPROC<br />+ .previous<br />+<br />+ <br /> /*<br /> * APIC interrupts.<br /> */ <br />&#64;&#64; -826,7 +842,10 &#64;&#64; paranoid_swapgs: <br /> swapgs<br /> paranoid_restore: <br /> RESTORE_ALL 8<br />- iretq<br />+1: iretq<br />+ .section __ex_table,"a"<br />+ .quad 1b,bad_iret<br />+ .previous<br /> paranoid_userspace: <br /> cli<br /> GET_THREAD_INFO(%rcx)<br />--- linux-2.6/arch/x86_64/kernel/traps.c<br />+++ linux-2.6/arch/x86_64/kernel/traps.c<br />&#64;&#64; -404,6 +404,44 &#64;&#64; void die_nmi(char *str, struct pt_regs *<br /> do_exit(SIGSEGV);<br /> }<br /> <br />+<br />+/*<br />+ * When we get an exception in the iret instructions in entry.S, whatever<br />+ * fault it is really belongs to the user state we are restoring. We want<br />+ * to turn it into a signal. To make that signal's info exactly match what<br />+ * this same kind of fault in a user instruction would show, the fixup<br />+ * needs to know the trapno and error code. But those are lost when we get<br />+ * back to the fixup entrypoint. So we have a special case for the iret<br />+ * fixups, and generate the signal here like a normal user trap would.<br />+ * Then the fixup code restores the pt_regs on the base of the stack to<br />+ * the bogus user state it was trying to return to, before handling the signal.<br />+ */<br />+extern void bad_iret(void); /* entry.S label for code in .fixup */<br />+static inline int is_iret(struct pt_regs *regs)<br />+{<br />+ return (regs-&gt;rip == (unsigned long)&amp;bad_iret);<br />+}<br />+<br />+/*<br />+ * If the iret was actually trying to return to kernel mode,<br />+ * that should be an oops.<br />+ */<br />+static inline int iret_to_user(struct pt_regs *regs)<br />+{<br />+ /*<br />+ * The frame being restored was all popped off and restored except<br />+ * the last five words that iret pops. Instead of popping, it<br />+ * pushed another trap frame, clobbering the part of the old one<br />+ * that we had already restored. So the restored registers are now<br />+ * all back in the new trap frame, but the rip et al show the<br />+ * in-kernel state at the iret instruction. The bad state we tried<br />+ * to restore with iret is still on the old stack below.<br />+ */<br />+ struct pt_regs *oregs = container_of((unsigned long *) regs-&gt;rsp,<br />+ struct pt_regs, rip);<br />+<br />+ return likely((oregs-&gt;cs &amp; 3) == 3);<br />+}<br /> static void do_trap(int trapnr, int signr, char *str, <br /> struct pt_regs * regs, long error_code, siginfo_t *info)<br /> {<br />&#64;&#64; -423,7 +461,9 &#64;&#64; static void do_trap(int trapnr, int sign<br /> #endif<br /> <br /> if ((regs-&gt;cs &amp; 3) != 0) { <br />- struct task_struct *tsk = current;<br />+ struct task_struct *tsk;<br />+ user_trap:<br />+ tsk = current;<br /> <br /> if (exception_trace &amp;&amp; unhandled_signal(tsk, signr))<br /> printk(KERN_INFO<br />&#64;&#64; -447,7 +487,13 &#64;&#64; static void do_trap(int trapnr, int sign<br /> fixup = search_exception_tables(regs-&gt;rip);<br /> if (fixup) {<br /> regs-&gt;rip = fixup-&gt;fixup;<br />- } else <br />- die(str, regs, error_code);<br />+ if (!is_iret(regs))<br />+ return;<br />+ if (iret_to_user(regs)) {<br />+ local_irq_enable();<br />+ goto user_trap;<br />+ }<br />+ }<br />+ die(str, regs, error_code);<br /> return;<br /> }<br />&#64;&#64; -523,8 +569,10 &#64;&#64; asmlinkage void do_general_protection(st<br /> }<br /> #endif<br /> <br />- if ((regs-&gt;cs &amp; 3)!=0) { <br />- struct task_struct *tsk = current;<br />+ if ((regs-&gt;cs &amp; 3) != 0) {<br />+ struct task_struct *tsk;<br />+ user_gp:<br />+ tsk = current;<br /> <br /> if (exception_trace &amp;&amp; unhandled_signal(tsk, SIGSEGV))<br /> printk(KERN_INFO<br />&#64;&#64; -544,7 +592,12 &#64;&#64; asmlinkage void do_general_protection(st<br /> fixup = search_exception_tables(regs-&gt;rip);<br /> if (fixup) {<br /> regs-&gt;rip = fixup-&gt;fixup;<br />- return;<br />+ if (!is_iret(regs))<br />+ return;<br />+ if (iret_to_user(regs)) {<br />+ local_irq_enable();<br />+ goto user_gp;<br />+ }<br /> }<br /> if (notify_die(DIE_GPF, "general protection fault", regs,<br /> error_code, 13, SIGSEGV) == NOTIFY_STOP)<br />-<br />To unsubscribe from this list: send the line "unsubscribe linux-kernel" in<br />the body of a message to majordomo&#64;vger.kernel.org<br />More majordomo info at <a href="http://vger.kernel.org/majordomo-info.html">http://vger.kernel.org/majordomo-info.html</a><br />Please read the FAQ at <a href="http://www.tux.org/lkml/">http://www.tux.org/lkml/</a><br /></pre></td><td width="32" rowspan="2" class="c" valign="top"><img src="/images/icornerr.gif" width="32" height="32" alt="\" /></td></tr><tr><td align="right" valign="bottom"> 聽 </td></tr><tr><td align="right" valign="bottom">聽</td><td class="c" valign="bottom" style="padding-bottom: 0px"><img src="/images/bcornerl.gif" width="32" height="32" alt="\" /></td><td class="c">聽</td><td class="c" valign="bottom" style="padding-bottom: 0px"><img src="/images/bcornerr.gif" width="32" height="32" alt="/" /></td></tr><tr><td align="right" valign="top" colspan="2"> 聽 </td><td class="lm">Last update: 2005-04-24 02:55 聽聽 [from the cache]<br />漏2003-2020 <a href="http://blog.jasper.es/"><span itemprop="editor">Jasper Spaans</span></a>|hosted at <a href="https://www.digitalocean.com/?refcode=9a8e99d24cf9">Digital Ocean</a> and my Meterkast|<a href="http://blog.jasper.es/categories.html#lkml-ref">Read the blog</a></td><td>聽</td></tr></table><script language="javascript" src="/js/styleswitcher.js" type="text/javascript"></script></body></html>

Pages: 1 2 3 4 5 6 7 8 9 10