Skip to contentSkip to conversation

RIXI fixes.

PATCH In review Ralf Baechle 2 participants 6 messages Jul 23, 2015 MIPS

lore
Share & tools
lore
#4 lore raw

PATCH 2/2 MIPS: Partially disable RIXI support.

In reply to Ralf Baechle

Execution of break instruction, trap instructions, emulation of unaligned loads or floating point instructions - anything that tries to read the instruction's opcode from userspace - needs read access to a page.

RIXI (Read Inhibit / Execute Inhibit) support however allows the creation of pags that are executable but not readable. On such a mapping the attempted load of the opcode by the kernel is going to cause an endless loop of page faults.

The quick workaround for this is to disable the combinations that the kernel currently isn't able to handle which are executable mappings.

---
 arch/mips/mm/cache.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
arch/mips/mm/cache.c +4 -4
index 77d96db..aab218c 100644
@@ -160,18 +160,18 @@ static inline void setup_protection_map(void)
 		protection_map[1]  = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_EXEC);
 		protection_map[2]  = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_EXEC | _PAGE_NO_READ);
 		protection_map[3]  = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_EXEC);
-		protection_map[4]  = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_READ);
+		protection_map[4]  = __pgprot(_page_cachable_default | _PAGE_PRESENT);
 		protection_map[5]  = __pgprot(_page_cachable_default | _PAGE_PRESENT);
-		protection_map[6]  = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_READ);
+		protection_map[6]  = __pgprot(_page_cachable_default | _PAGE_PRESENT);
 		protection_map[7]  = __pgprot(_page_cachable_default | _PAGE_PRESENT);
 
 		protection_map[8]  = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_EXEC | _PAGE_NO_READ);
 		protection_map[9]  = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_EXEC);
 		protection_map[10] = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_EXEC | _PAGE_WRITE | _PAGE_NO_READ);
 		protection_map[11] = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_EXEC | _PAGE_WRITE);
-		protection_map[12] = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_READ);
+		protection_map[12] = __pgprot(_page_cachable_default | _PAGE_PRESENT);
 		protection_map[13] = __pgprot(_page_cachable_default | _PAGE_PRESENT);
-		protection_map[14] = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_WRITE  | _PAGE_NO_READ);
+		protection_map[14] = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_WRITE);
 		protection_map[15] = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_WRITE);
 
 	} else {
Signature · 1 line
2.4.3
#1 lore raw

PATCH 1/2 MIPS: Handle page faults of executable but unreadable pages correctly.

In reply to Ralf Baechle

Without this we end taking execeptions in an endless loop hanging the thread.

---
 arch/mips/mm/fault.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
arch/mips/mm/fault.c +2 -1
index 36c0f26..852a41c 100644
@@ -133,7 +133,8 @@ good_area:
 #endif
 				goto bad_area;
 			}
-			if (!(vma->vm_flags & VM_READ)) {
+			if (!(vma->vm_flags & VM_READ) &&
+			    exception_epc(regs) != address) {
 #if 0
 				pr_notice("Cpu%d[%s:%d:%0*lx:%ld:%0*lx] RI violation\n",
 					  raw_smp_processor_id(),
Signature · 1 line
2.4.3
#0 lore raw

cover RIXI fixes.

As part of unrelated work I ran into issus with RIXI support which allows the creation of pages that are executable but not readable. Problems arise with instruction emulation on such pages, for example when a load or store instruction is taking an unaligned exception, is an FPU instruction that requires emulation, a trap or break instruction of which the break code needs to bread and more.

A better solution would be to create a temporary mapping using kmap_coherent() but that's more complex so left for a later stage.

These patches have been sitting in a shady git tree for years but seem to work for me. Nevertheless Comments appreciated.

Cheers,

Ralf

Ralf Baechle (2):
MIPS: Handle page faults of executable but unreadable pages correctly.
MIPS: Partially disable RIXI support.

 arch/mips/mm/cache.c | 8 ++++----
 arch/mips/mm/fault.c | 3 ++-
 2 files changed, 6 insertions(+), 5 deletions(-)
Signature · 1 line
2.4.3
Reviewed-by on v1 2/2
#5 lore raw

Re: [PATCH 2/2] MIPS: Partially disable RIXI support.

In reply to Ralf Baechle
Quoted 9 lines · Ralf Baechle

Execution of break instruction, trap instructions, emulation of unaligned loads or floating point instructions - anything that tries to read the instruction's opcode from userspace - needs read access to a page.

RIXI (Read Inhibit / Execute Inhibit) support however allows the creation of pags that are executable but not readable. On such a mapping the attempted load of the opcode by the kernel is going to cause an endless loop of page faults.

Quoted 4 lines

The quick workaround for this is to disable the combinations that the kernel currently isn't able to handle which are executable mappings.

Cheers
James

Quoted 32 lines
---
 arch/mips/mm/cache.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
arch/mips/mm/cache.c +4 -4
index 77d96db..aab218c 100644
@@ -160,18 +160,18 @@ static inline void setup_protection_map(void)
 		protection_map[1]  = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_EXEC);
 		protection_map[2]  = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_EXEC | _PAGE_NO_READ);
 		protection_map[3]  = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_EXEC);
-		protection_map[4]  = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_READ);
+		protection_map[4]  = __pgprot(_page_cachable_default | _PAGE_PRESENT);
 		protection_map[5]  = __pgprot(_page_cachable_default | _PAGE_PRESENT);
-		protection_map[6]  = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_READ);
+		protection_map[6]  = __pgprot(_page_cachable_default | _PAGE_PRESENT);
 		protection_map[7]  = __pgprot(_page_cachable_default | _PAGE_PRESENT);
 
 		protection_map[8]  = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_EXEC | _PAGE_NO_READ);
 		protection_map[9]  = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_EXEC);
 		protection_map[10] = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_EXEC | _PAGE_WRITE | _PAGE_NO_READ);
 		protection_map[11] = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_EXEC | _PAGE_WRITE);
-		protection_map[12] = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_READ);
+		protection_map[12] = __pgprot(_page_cachable_default | _PAGE_PRESENT);
 		protection_map[13] = __pgprot(_page_cachable_default | _PAGE_PRESENT);
-		protection_map[14] = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_WRITE  | _PAGE_NO_READ);
+		protection_map[14] = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_WRITE);
 		protection_map[15] = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_WRITE);
 
 	} else {
#2 lore raw

Re: [PATCH 1/2] MIPS: Handle page faults of executable but unreadable pages correctly.

In reply to Ralf Baechle
Quoted 2 lines · Ralf Baechle

Without this we end taking execeptions in an endless loop hanging the thread.

A little more explanation would be nice. Under what situations does this occur? Does this mean any VM_EXEC and !VM_READ page can't actually be faulted in without it being treated as an RI violation, or does it only affect when read from kernel emulation code?

Cheers
James

Quoted 21 lines
---
 arch/mips/mm/fault.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
arch/mips/mm/fault.c +2 -1
index 36c0f26..852a41c 100644
@@ -133,7 +133,8 @@ good_area:
 #endif
 				goto bad_area;
 			}
-			if (!(vma->vm_flags & VM_READ)) {
+			if (!(vma->vm_flags & VM_READ) &&
+			    exception_epc(regs) != address) {
 #if 0
 				pr_notice("Cpu%d[%s:%d:%0*lx:%ld:%0*lx] RI violation\n",
 					  raw_smp_processor_id(),
#3 lore raw

Re: [PATCH 1/2] MIPS: Handle page faults of executable but unreadable pages correctly.

Quoted 9 lines · James Hogan
Quoted 2 lines · Ralf Baechle

Without this we end taking execeptions in an endless loop hanging the thread.

A little more explanation would be nice. Under what situations does this occur? Does this mean any VM_EXEC and !VM_READ page can't actually be faulted in without it being treated as an RI violation, or does it only affect when read from kernel emulation code?

Quoted 8 lines

arch/mips/mm/fault.c good_area: · line 140

Quoted 7 lines
arch/mips/mm/fault.c +2 -1
-			if (!(vma->vm_flags & VM_READ)) {
+			if (!(vma->vm_flags & VM_READ) &&
+			    exception_epc(regs) != address) {
 #if 0
 				pr_notice("Cpu%d[%s:%d:%0*lx:%ld:%0*lx] RI violation\n",
 					  raw_smp_processor_id(),

The general idea is the change the code to treat loads of an instruction just like an instruction fetch. Which is achieved by adding the second condition "exception_epc(regs) != address" to the if above. exception_epc(regs) == address means

It would all be easier if Linux was enabling the separate exception codes for read and execution failure but short of that, a test like above must provide if a fault was an attempted instruction fetch or happend fetching data.

Ralf