asamy/ksm

Wrongly computed MTRR range addresses

wbenny opened this issue · 16 comments

Type of this issue (please specify)

  • This is a bug in the upstream tree as-is unmodified.
  • This is a support matter (i.e. your own modified tree)
  • This is a technical question

In mm.c, function mm_cache_mtrr_ranges

...
	if ((cap >> 8) & 1 && (def >> 10) & 1) {
		/* Read fixed range MTRRs.  */
		for (msr = __readmsr(MSR_MTRRfix64K_00000), offset = 0, base = 0;
		     msr != 0; msr >>= 8, offset += 0x10000, base += offset)
			make_mtrr_range(&ranges[idx++], true, msr & 0xff, base, base + 0x10000 - 1);

		for (val = MSR_MTRRfix16K_80000, offset = 0; val <= MSR_MTRRfix16K_A0000; ++val)
			for (msr = __readmsr(val), base = 0x80000;
			     msr != 0; msr >>= 8, offset += 0x4000, base += offset)
				make_mtrr_range(&ranges[idx++], true, msr & 0xff, base, base + 0x4000 - 1);

		for (val = MSR_MTRRfix4K_C0000, offset = 0; val <= MSR_MTRRfix4K_F8000; ++val)
			for (msr = __readmsr(val), base = 0xC0000;
			     msr != 0; msr >>= 8, offset += 0x1000, base += offset)
				make_mtrr_range(&ranges[idx++], true, msr & 0xff, base, base + 0x1000 - 1);
	}
...

offset should not be incremented each inner loop - it should be set once (with base, in the initial part of the for expression).

This error creates this pattern:

ksm: CPU 1: ksm_init: MTRR Range: 0x0000000000000000 -> 0x000000000000FFFF fixed: 1 type: 6
ksm: CPU 1: ksm_init: MTRR Range: 0x0000000000010000 -> 0x000000000001FFFF fixed: 1 type: 6
ksm: CPU 1: ksm_init: MTRR Range: 0x0000000000030000 -> 0x000000000003FFFF fixed: 1 type: 6 // should be 0x0000000000020000 -> 0x000000000002FFFF
ksm: CPU 1: ksm_init: MTRR Range: 0x0000000000060000 -> 0x000000000006FFFF fixed: 1 type: 6 // should be 0x0000000000030000 -> 0x000000000003FFFF
ksm: CPU 1: ksm_init: MTRR Range: 0x00000000000A0000 -> 0x00000000000AFFFF fixed: 1 type: 6 // etc
ksm: CPU 1: ksm_init: MTRR Range: 0x00000000000F0000 -> 0x00000000000FFFFF fixed: 1 type: 6 // ...
asamy commented

Can you send a patch?

sorry, been quite lazy :) here you go:

From 176404f6f7ba873f0d314c65f4093f6a9fce04b7 Mon Sep 17 00:00:00 2001
From: Petr Benes <w.benny@outlook.com>
Date: Sun, 13 May 2018 00:39:27 +0200
Subject: [PATCH] fix wrongly computed mtrr range addresses

---
 mm.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/mm.c b/mm.c
index d6ee8df..9e5ef0d 100644
--- a/mm.c
+++ b/mm.c
@@ -204,18 +204,18 @@ void mm_cache_mtrr_ranges(struct mtrr_range *ranges, int *range_count, u8 *def_t
 
 	if ((cap >> 8) & 1 && (def >> 10) & 1) {
 		/* Read fixed range MTRRs.  */
-		for (msr = __readmsr(MSR_MTRRfix64K_00000), offset = 0, base = 0;
-		     msr != 0; msr >>= 8, offset += 0x10000, base += offset)
+		for (msr = __readmsr(MSR_MTRRfix64K_00000), offset = 0x10000, base = 0;
+		     msr != 0; msr >>= 8, base += offset)
 			make_mtrr_range(&ranges[idx++], true, msr & 0xff, base, base + 0x10000 - 1);
 
-		for (val = MSR_MTRRfix16K_80000, offset = 0; val <= MSR_MTRRfix16K_A0000; ++val)
+		for (val = MSR_MTRRfix16K_80000, offset = 0x4000; val <= MSR_MTRRfix16K_A0000; ++val)
 			for (msr = __readmsr(val), base = 0x80000;
-			     msr != 0; msr >>= 8, offset += 0x4000, base += offset)
+			     msr != 0; msr >>= 8, base += offset)
 				make_mtrr_range(&ranges[idx++], true, msr & 0xff, base, base + 0x4000 - 1);
 
-		for (val = MSR_MTRRfix4K_C0000, offset = 0; val <= MSR_MTRRfix4K_F8000; ++val)
+		for (val = MSR_MTRRfix4K_C0000, offset = 0x1000; val <= MSR_MTRRfix4K_F8000; ++val)
 			for (msr = __readmsr(val), base = 0xC0000;
-			     msr != 0; msr >>= 8, offset += 0x1000, base += offset)
+			     msr != 0; msr >>= 8, base += offset)
 				make_mtrr_range(&ranges[idx++], true, msr & 0xff, base, base + 0x1000 - 1);
 	}
 
-- 
2.17.0.windows.1

asamy commented

That's a diff not a patch :(
Generate with:
git format-patch HEAD~

edited

asamy commented

Applied, thanks!

You probably also want to apply this. in_bounds checks for gpa < end, not gpa <= end. mm_cache_ram_ranges does this correctly.

From dcf30c903ab99f258c02f6a70f5096f5f5ae0cce Mon Sep 17 00:00:00 2001
From: Petr Benes <w.benny@outlook.com>
Date: Sun, 13 May 2018 01:11:01 +0200
Subject: [PATCH] fix off-by-one error in making of mtrr range

---
 mm.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/mm.c b/mm.c
index 9e5ef0d..34a75ce 100644
--- a/mm.c
+++ b/mm.c
@@ -206,17 +206,17 @@ void mm_cache_mtrr_ranges(struct mtrr_range *ranges, int *range_count, u8 *def_t
 		/* Read fixed range MTRRs.  */
 		for (msr = __readmsr(MSR_MTRRfix64K_00000), offset = 0x10000, base = 0;
 		     msr != 0; msr >>= 8, base += offset)
-			make_mtrr_range(&ranges[idx++], true, msr & 0xff, base, base + 0x10000 - 1);
+			make_mtrr_range(&ranges[idx++], true, msr & 0xff, base, base + 0x10000);
 
 		for (val = MSR_MTRRfix16K_80000, offset = 0x4000; val <= MSR_MTRRfix16K_A0000; ++val)
 			for (msr = __readmsr(val), base = 0x80000;
 			     msr != 0; msr >>= 8, base += offset)
-				make_mtrr_range(&ranges[idx++], true, msr & 0xff, base, base + 0x4000 - 1);
+				make_mtrr_range(&ranges[idx++], true, msr & 0xff, base, base + 0x4000);
 
 		for (val = MSR_MTRRfix4K_C0000, offset = 0x1000; val <= MSR_MTRRfix4K_F8000; ++val)
 			for (msr = __readmsr(val), base = 0xC0000;
 			     msr != 0; msr >>= 8, base += offset)
-				make_mtrr_range(&ranges[idx++], true, msr & 0xff, base, base + 0x1000 - 1);
+				make_mtrr_range(&ranges[idx++], true, msr & 0xff, base, base + 0x1000);
 	}
 
 	for (i = 0; i < num_var; i++) {
-- 
2.17.0.windows.1

asamy commented

Makes sense that way I guess. Thanks!

Glad I've helped! Maybe that's the reason of the two bugs sitting in the issue page for so long.
Also, umm... I've edited the email in the last patch, I mistakenly used the one for the work. Ummm... do you think you can edit & force push that, please? :|

asamy commented

Hah, I noticed that too, done.

Thanks!

I swear this is the last one... :)

From fcc0e8cdb8701be7feb01542093d985ebc7a12cd Mon Sep 17 00:00:00 2001
From: Petr Benes <w.benny@outlook.com>
Date: Sun, 13 May 2018 01:40:43 +0200
Subject: [PATCH] another off-by-one error

---
 mm.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mm.c b/mm.c
index 34a75ce..eef5511 100644
--- a/mm.c
+++ b/mm.c
@@ -229,7 +229,7 @@ void mm_cache_mtrr_ranges(struct mtrr_range *ranges, int *range_count, u8 *def_t
 		make_mtrr_range(&ranges[idx++], false,
 				base & 0xff,
 				base & PAGE_PA_MASK,
-				(base & PAGE_PA_MASK) + len - 1);
+				(base & PAGE_PA_MASK) + len);
 	}
 
 	*range_count = idx;
-- 
2.17.0.windows.1

asamy commented

Thanks!

asamy commented

This fixes #23 and #22 right?

Honestly, I don't know, I couldn't reproduce these bugs. Just a wild guess. But I was referring to them, yes. :) Maybe time to bump these issues up and summon the authors back to try it.

Hi again, previous fixes were not sufficient. In the loop where you've been filling fixed MTRRs, the exit condition checked for msr != 0. But if fixed MSR contained eight EPT_MT_UNCACHABLE sub-ranges, the MSR value has been already 0 (therefore the whole loop has been skipped). I've checked that maybe ept_memory_type function accounts for it, but it doesn't (and on a second thought, it even can't).

So, here's another patch :)

From 20cfc80b251c6d1da5d516711fdbaae8e1a0e1d4 Mon Sep 17 00:00:00 2001
From: Petr Benes <w.benny@outlook.com>
Date: Tue, 24 Jul 2018 01:13:40 +0200
Subject: [PATCH] fix wrongly computed MTRR (again)

---
 mm.c | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/mm.c b/mm.c
index eef5511..bf53c03 100644
--- a/mm.c
+++ b/mm.c
@@ -204,19 +204,19 @@ void mm_cache_mtrr_ranges(struct mtrr_range *ranges, int *range_count, u8 *def_t
 
 	if ((cap >> 8) & 1 && (def >> 10) & 1) {
 		/* Read fixed range MTRRs.  */
-		for (msr = __readmsr(MSR_MTRRfix64K_00000), offset = 0x10000, base = 0;
-		     msr != 0; msr >>= 8, base += offset)
-			make_mtrr_range(&ranges[idx++], true, msr & 0xff, base, base + 0x10000);
+		for (msr = __readmsr(MSR_MTRRfix64K_00000), offset = 0x10000, base = 0, i = 0;
+		     i < 8; msr >>= 8, base += offset, ++i)
+			make_mtrr_range(&ranges[idx++], true, msr & 0xff, base, base + offset);
 
 		for (val = MSR_MTRRfix16K_80000, offset = 0x4000; val <= MSR_MTRRfix16K_A0000; ++val)
-			for (msr = __readmsr(val), base = 0x80000;
-			     msr != 0; msr >>= 8, base += offset)
-				make_mtrr_range(&ranges[idx++], true, msr & 0xff, base, base + 0x4000);
+			for (msr = __readmsr(val), base = 0x80000, i = 0;
+			     i < 8; msr >>= 8, base += offset, ++i)
+				make_mtrr_range(&ranges[idx++], true, msr & 0xff, base, base + offset);
 
 		for (val = MSR_MTRRfix4K_C0000, offset = 0x1000; val <= MSR_MTRRfix4K_F8000; ++val)
-			for (msr = __readmsr(val), base = 0xC0000;
-			     msr != 0; msr >>= 8, base += offset)
-				make_mtrr_range(&ranges[idx++], true, msr & 0xff, base, base + 0x1000);
+			for (msr = __readmsr(val), base = 0xC0000, i = 0;
+			     i < 8; msr >>= 8, base += offset, ++i)
+				make_mtrr_range(&ranges[idx++], true, msr & 0xff, base, base + offset);
 	}
 
 	for (i = 0; i < num_var; i++) {
-- 
2.17.0.windows.1

Well, it looks like variable MTRR computation is wrong as well:

From 10c7c3bba6be80ace724922f199d5ba206522abf Mon Sep 17 00:00:00 2001
From: Petr Benes <w.benny@outlook.com>
Date: Tue, 24 Jul 2018 02:39:58 +0200
Subject: [PATCH] fix wrongly computed MTRR (this time - variable)

---
 mm.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/mm.c b/mm.c
index bf53c03..46be919 100644
--- a/mm.c
+++ b/mm.c
@@ -194,7 +194,7 @@ void mm_cache_mtrr_ranges(struct mtrr_range *ranges, int *range_count, u8 *def_t
 	int num_var;
 	int idx = 0;
 	int i;
-	u32 len;
+	u64 len;
 
 	def = __readmsr(MSR_MTRRdefType);
 	*def_type = def & 0xFF;
@@ -224,7 +224,7 @@ void mm_cache_mtrr_ranges(struct mtrr_range *ranges, int *range_count, u8 *def_t
 		if (!((msr >> 11) & 1))
 			continue;
 
-		len = 1 << __ffs64(msr & PAGE_PA_MASK);
+		len = 1ull << __ffs64(msr & PAGE_PA_MASK);
 		base = __readmsr(MSR_MTRR_PHYS_BASE + i * 2);
 		make_mtrr_range(&ranges[idx++], false,
 				base & 0xff,
-- 
2.17.0.windows.1
asamy commented

Thanks, applied.