public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Chris Wright <chrisw@osdl.org>
To: Zachary Amsden <zach@vmware.com>
Cc: Chris Wright <chrisw@osdl.org>,
	akpm@osdl.org, chrisl@vmware.com, hpa@zytor.com,
	linux-kernel@vger.kernel.org, mbligh@mbligh.org,
	pratap@vmware.com, virtualization@lists.osdl.org,
	zwane@arm.linux.org.uk
Subject: Re: [PATCH 3/6] i386 virtualization - Make ldt a desc struct
Date: Mon, 15 Aug 2005 23:17:49 -0700	[thread overview]
Message-ID: <20050816061749.GM7991@shell0.pdx.osdl.net> (raw)
In-Reply-To: <43017DB1.6010506@vmware.com>

* Zachary Amsden (zach@vmware.com) wrote:
> Chris Wright wrote:
> >>@@ -30,7 +33,7 @@
> >>static inline unsigned long get_desc_base(struct desc_struct *desc)
> >>{
> >>	unsigned long base;
> >>-	base = ((desc->a >> 16)  & 0x0000ffff) |
> >>+	base = (desc->a >> 16) |
> >
> >Seemingly unrelated.
> 
> Yes, alas my bucket has leaks.  I was hoping for better assembly, but 
> never got around to verifying.  So I matched this to shorter C code 
> which I had obsoleted.

OK.

> >>@@ -28,28 +28,27 @@
> >>}
> >>#endif
> >>
> >>-static inline int alloc_ldt(mm_context_t *pc, const int oldsize, int 
> >>mincount, const int reload)
> >>+static inline int alloc_ldt(mm_context_t *pc, const int old_pages, int 
> >>new_pages, const int reload)
> >>{
> >>-	void *oldldt;
> >>-	void *newldt;
> >>+	struct desc_struct *oldldt;
> >>+	struct desc_struct *newldt;
> >>
> >
> >Not quite related here (since change was introduced in earlier patch),
> >but old alloc_ldt special cased when room was available.  This is gone,
> >so am I reading this correctly, each time through it will allocate a
> >new one, and free the old one (if it existed)?  Just double checking
> >that change doesn't introduce possible mem leak.
> >
> 
> Since LDT is now in pages, it is only called when page reservation 
> increases.   I chose a slightly bad name here for new_pages.  See 
> further down:
> 
>        if (page_number >= mm->context.ldt_pages) {

OK, nice, I had missed that.

>                error = alloc_ldt(&current->mm->context, 
> mm->context.ldt_pages,
>                                        page_number+1, 1);
>                if (error < 0)
>                        goto out_unlock;
>        }
> 
> I actually had to check the code here to verify there is no leak, and I 
> don't believe I changed any semantics, but was very happy when I found this:
> 
>        if (old_pages) {
>                ClearPagesLDT(oldldt, old_pages);
>                if (old_pages > 1)
>                        vfree(oldldt);
>                else
>                        kfree(oldldt);
>        }

Yeah, I saw that bit too, so I was assuming it was OK, and wanted to
double-check.

> >>-	mincount = (mincount+511)&(~511);
> >>-	if (mincount*LDT_ENTRY_SIZE > PAGE_SIZE)
> >>-		newldt = vmalloc(mincount*LDT_ENTRY_SIZE);
> >>+	if (new_pages > 1)
> >>+		newldt = vmalloc(new_pages*PAGE_SIZE);
> >>	else
> >>-		newldt = kmalloc(mincount*LDT_ENTRY_SIZE, GFP_KERNEL);
> >>+		newldt = kmalloc(PAGE_SIZE, GFP_KERNEL);
> >
> >If so, then full page is likely to be reusable in common case, no? (If
> >there's such a thing as LDT common case ;-)
> 
> Yeah, there is no LDT common case.  This code could be made 100% optimal 
> with a lot of likely/unlikely wrappers and additional cleanup, but 
> seeing as it is already uncommon, the only worthwhile optimizations for 
> this code are ones that reduce code size or make it more readable and 
> less error prone.  I had to write a test that emits inline assembler 
> onto a crossing page boundary, clones the VM, and tests strict 
> conformance to byte/page limits to actually test this.  :)

Ouch, sounds painful ;-)

> >>	if (!newldt)
> >>		return -ENOMEM;
> >>
> >>-	if (oldsize)
> >>-		memcpy(newldt, pc->ldt, oldsize*LDT_ENTRY_SIZE);
> >>+	if (old_pages)
> >>+		memcpy(newldt, pc->ldt, old_pages*PAGE_SIZE);
> >>	oldldt = pc->ldt;
> >>	if (reload)
> >>-		memset(newldt+oldsize*LDT_ENTRY_SIZE, 0, 
> >>(mincount-oldsize)*LDT_ENTRY_SIZE);
> >>+		memset(newldt+old_pages*LDT_ENTRIES_PER_PAGE, 0, 
> >>(new_pages-old_pages)*PAGE_SIZE);
> >
> >In fact, I _think_ this causes a problem.  Who says newldt is bigger
> >than old one?  This looks like user-triggerable oops to me.
> 
> Safe -- two call sites.  One has no LDT (cloning), and the other is here:
> 
>        if (page_number >= mm->context.ldt_pages) {

Yes, thanks, as I mentioned above, that's what I was missing.

>                error = alloc_ldt(&current->mm->context, 
> mm->context.ldt_pages,
> 
> Note page_number is zero-based, ldt_pages is not.
> 
> >>@@ -113,13 +114,13 @@
> >>	unsigned long size;
> >>	struct mm_struct * mm = current->mm;
> >>
> >>-	if (!mm->context.size)
> >>+	if (!mm->context.ldt_pages)
> >>		return 0;
> >>	if (bytecount > LDT_ENTRY_SIZE*LDT_ENTRIES)
> >>		bytecount = LDT_ENTRY_SIZE*LDT_ENTRIES;
> >>
> >>	down(&mm->context.sem);
> >>-	size = mm->context.size*LDT_ENTRY_SIZE;
> >>+	size = mm->context.ldt_pages*PAGE_SIZE;
> >>	if (size > bytecount)
> >>		size = bytecount;
> >
> >This now looks like you can leak data?  Since full page is unlikely
> >used, but accounting is done in page sizes.  Asking to read_ldt with
> >bytcount of PAGE_SIZE could give some uninitialzed data back to user.
> >Did I miss the spot where this is always zero-filled?
> 
> You could leak data, but the code already takes care to zero the page.  
> This is especially important, since random present segments could allow 
> a violation of kernel security ;)

Right, good point.

>        if (reload)
>                memset(newldt+old_pages*LDT_ENTRIES_PER_PAGE, 0, 
> (new_pages-old_pages)*PAGE_SIZE);

Ah, I misread reload as being same arg as oldmode in write_ldt(), so
had myself thinking that was user controlled.

> Wow.  Thanks for a completely thorough review.  I have tested this code 
> quite intensely, but I very much appreciate having more eyes on it, 
> since it is quite a tricky biscuit.

Agreed, the more eyes the better.

thanks,
-chris

  reply	other threads:[~2005-08-16  6:17 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2005-08-15 22:59 [PATCH 3/6] i386 virtualization - Make ldt a desc struct zach
2005-08-16  5:23 ` Chris Wright
2005-08-16  5:46   ` Zachary Amsden
2005-08-16  6:17     ` Chris Wright [this message]
  -- strict thread matches above, loose matches on Subject: below --
2005-08-16 17:03 Chuck Ebbert
2005-08-16 18:44 ` Zachary Amsden
2005-08-16 20:41   ` Chris Wright
2005-08-16 20:56     ` Zachary Amsden

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20050816061749.GM7991@shell0.pdx.osdl.net \
    --to=chrisw@osdl.org \
    --cc=akpm@osdl.org \
    --cc=chrisl@vmware.com \
    --cc=hpa@zytor.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mbligh@mbligh.org \
    --cc=pratap@vmware.com \
    --cc=virtualization@lists.osdl.org \
    --cc=zach@vmware.com \
    --cc=zwane@arm.linux.org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox