cregit-Linux how code gets into the kernel

Release 4.11 arch/x86/kernel/ldt.c

Directory: arch/x86/kernel
/*
 * Copyright (C) 1992 Krishna Balasubramanian and Linus Torvalds
 * Copyright (C) 1999 Ingo Molnar <mingo@redhat.com>
 * Copyright (C) 2002 Andi Kleen
 *
 * This handles calls from both 32bit and 64bit mode.
 */

#include <linux/errno.h>
#include <linux/gfp.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/uaccess.h>

#include <asm/ldt.h>
#include <asm/desc.h>
#include <asm/mmu_context.h>
#include <asm/syscalls.h>

/* context.lock is held for us, so we don't need any locking. */

static void flush_ldt(void *current_mm) { mm_context_t *pc; if (current->active_mm != current_mm) return; pc = &current->active_mm->context; set_ldt(pc->ldt->entries, pc->ldt->size); }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Lutomirski2246.81%125.00%
Dave Jones2144.68%125.00%
Jan Beulich24.26%125.00%
Manfred Spraul24.26%125.00%
Total47100.00%4100.00%

/* The caller must call finalize_ldt_struct on the result. LDT starts zeroed. */
static struct ldt_struct *alloc_ldt_struct(unsigned int size) { struct ldt_struct *new_ldt; unsigned int alloc_size; if (size > LDT_ENTRIES) return NULL; new_ldt = kmalloc(sizeof(struct ldt_struct), GFP_KERNEL); if (!new_ldt) return NULL; BUILD_BUG_ON(LDT_ENTRY_SIZE != sizeof(struct desc_struct)); alloc_size = size * LDT_ENTRY_SIZE; /* * Xen is very picky: it requires a page-aligned LDT that has no * trailing nonzero bytes in any page that contains LDT descriptors. * Keep it simple: zero the whole allocation and never allocate less * than PAGE_SIZE. */ if (alloc_size > PAGE_SIZE) new_ldt->entries = vzalloc(alloc_size); else new_ldt->entries = (void *)get_zeroed_page(GFP_KERNEL); if (!new_ldt->entries) { kfree(new_ldt); return NULL; } new_ldt->size = size; return new_ldt; }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Lutomirski7358.87%114.29%
Dave Jones4133.06%114.29%
Jan Beulich64.84%228.57%
Cyrill V. Gorcunov21.61%114.29%
Dan Carpenter10.81%114.29%
Thomas Gleixner10.81%114.29%
Total124100.00%7100.00%

/* After calling this, the LDT is immutable. */
static void finalize_ldt_struct(struct ldt_struct *ldt) { paravirt_alloc_ldt(ldt->entries, ldt->size); }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Lutomirski1568.18%125.00%
Dave Jones418.18%125.00%
Jeremy Fitzhardinge29.09%125.00%
Ingo Molnar14.55%125.00%
Total22100.00%4100.00%

/* context.lock is held */
static void install_ldt(struct mm_struct *current_mm, struct ldt_struct *ldt) { /* Synchronizes with lockless_dereference in load_mm_ldt. */ smp_store_release(&current_mm->context.ldt, ldt); /* Activate the LDT for all CPUs using current_mm. */ on_each_cpu_mask(mm_cpumask(current_mm), flush_ldt, current_mm, true); }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Lutomirski2965.91%120.00%
Dave Jones715.91%120.00%
Andrew Morton511.36%240.00%
Rusty Russell36.82%120.00%
Total44100.00%5100.00%


static void free_ldt_struct(struct ldt_struct *ldt) { if (likely(!ldt)) return; paravirt_free_ldt(ldt->entries, ldt->size); if (ldt->size * LDT_ENTRY_SIZE > PAGE_SIZE) vfree_atomic(ldt->entries); else free_page((unsigned long)ldt->entries); kfree(ldt); }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Lutomirski3249.23%120.00%
Dave Jones2132.31%120.00%
Jeremy Fitzhardinge69.23%120.00%
Jan Beulich57.69%120.00%
Andrey Ryabinin11.54%120.00%
Total65100.00%5100.00%

/* * we do not have to muck with descriptors here, that is * done in switch_mm() as needed. */
int init_new_context_ldt(struct task_struct *tsk, struct mm_struct *mm) { struct ldt_struct *new_ldt; struct mm_struct *old_mm; int retval = 0; mutex_init(&mm->context.lock); old_mm = current->mm; if (!old_mm) { mm->context.ldt = NULL; return 0; } mutex_lock(&old_mm->context.lock); if (!old_mm->context.ldt) { mm->context.ldt = NULL; goto out_unlock; } new_ldt = alloc_ldt_struct(old_mm->context.ldt->size); if (!new_ldt) { retval = -ENOMEM; goto out_unlock; } memcpy(new_ldt->entries, old_mm->context.ldt->entries, new_ldt->size * LDT_ENTRY_SIZE); finalize_ldt_struct(new_ldt); mm->context.ldt = new_ldt; out_unlock: mutex_unlock(&old_mm->context.lock); return retval; }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Lutomirski8448.55%120.00%
Dave Jones7442.77%120.00%
Jeremy Fitzhardinge105.78%120.00%
Luiz Fernando N. Capitulino42.31%120.00%
Dave Hansen10.58%120.00%
Total173100.00%5100.00%

/* * No need to lock the MM as we are the last user * * 64bit: Don't touch the LDT register - we're already in the next thread. */
void destroy_context_ldt(struct mm_struct *mm) { free_ldt_struct(mm->context.ldt); mm->context.ldt = NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Dave Jones1659.26%133.33%
Andrew Lutomirski1037.04%133.33%
Dave Hansen13.70%133.33%
Total27100.00%3100.00%


static int read_ldt(void __user *ptr, unsigned long bytecount) { int retval; unsigned long size; struct mm_struct *mm = current->mm; mutex_lock(&mm->context.lock); if (!mm->context.ldt) { retval = 0; goto out_unlock; } if (bytecount > LDT_ENTRY_SIZE * LDT_ENTRIES) bytecount = LDT_ENTRY_SIZE * LDT_ENTRIES; size = mm->context.ldt->size * LDT_ENTRY_SIZE; if (size > bytecount) size = bytecount; if (copy_to_user(ptr, mm->context.ldt->entries, size)) { retval = -EFAULT; goto out_unlock; } if (size != bytecount) { /* Zero-fill the rest and pretend we read bytecount bytes. */ if (clear_user(ptr + size, bytecount - size)) { retval = -EFAULT; goto out_unlock; } } retval = bytecount; out_unlock: mutex_unlock(&mm->context.lock); return retval; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)6939.88%763.64%
Andrew Lutomirski4626.59%19.09%
Dave Jones4224.28%19.09%
Jesper Juhl158.67%19.09%
Linus Torvalds10.58%19.09%
Total173100.00%11100.00%


static int read_default_ldt(void __user *ptr, unsigned long bytecount) { /* CHECKME: Can we use _one_ random number ? */ #ifdef CONFIG_X86_32 unsigned long size = 5 * sizeof(struct desc_struct); #else unsigned long size = 128; #endif if (bytecount > size) bytecount = size; if (clear_user(ptr, bytecount)) return -EFAULT; return bytecount; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds4262.69%240.00%
Thomas Gleixner2232.84%120.00%
Dave Jones22.99%120.00%
Jeremy Fitzhardinge11.49%120.00%
Total67100.00%5100.00%


static int write_ldt(void __user *ptr, unsigned long bytecount, int oldmode) { struct mm_struct *mm = current->mm; struct ldt_struct *new_ldt, *old_ldt; unsigned int oldsize, newsize; struct user_desc ldt_info; struct desc_struct ldt; int error; error = -EINVAL; if (bytecount != sizeof(ldt_info)) goto out; error = -EFAULT; if (copy_from_user(&ldt_info, ptr, sizeof(ldt_info))) goto out; error = -EINVAL; if (ldt_info.entry_number >= LDT_ENTRIES) goto out; if (ldt_info.contents == 3) { if (oldmode) goto out; if (ldt_info.seg_not_present == 0) goto out; } if ((oldmode && !ldt_info.base_addr && !ldt_info.limit) || LDT_empty(&ldt_info)) { /* The user wants to clear the entry. */ memset(&ldt, 0, sizeof(ldt)); } else { if (!IS_ENABLED(CONFIG_X86_16BIT) && !ldt_info.seg_32bit) { error = -EINVAL; goto out; } fill_ldt(&ldt, &ldt_info); if (oldmode) ldt.avl = 0; } mutex_lock(&mm->context.lock); old_ldt = mm->context.ldt; oldsize = old_ldt ? old_ldt->size : 0; newsize = max(ldt_info.entry_number + 1, oldsize); error = -ENOMEM; new_ldt = alloc_ldt_struct(newsize); if (!new_ldt) goto out_unlock; if (old_ldt) memcpy(new_ldt->entries, old_ldt->entries, oldsize * LDT_ENTRY_SIZE); new_ldt->entries[ldt_info.entry_number] = ldt; finalize_ldt_struct(new_ldt); install_ldt(mm, new_ldt); free_ldt_struct(old_ldt); error = 0; out_unlock: mutex_unlock(&mm->context.lock); out: return error; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)15945.69%637.50%
Andrew Lutomirski10831.03%16.25%
H. Peter Anvin226.32%16.25%
Glauber de Oliveira Costa216.03%212.50%
Thomas Gleixner154.31%16.25%
Zachary Amsden113.16%16.25%
Ingo Molnar72.01%16.25%
Dave Jones20.57%16.25%
Luiz Fernando N. Capitulino20.57%16.25%
Linus Torvalds10.29%16.25%
Total348100.00%16100.00%


asmlinkage int sys_modify_ldt(int func, void __user *ptr, unsigned long bytecount) { int ret = -ENOSYS; switch (func) { case 0: ret = read_ldt(ptr, bytecount); break; case 1: ret = write_ldt(ptr, bytecount, 1); break; case 2: ret = read_default_ldt(ptr, bytecount); break; case 0x11: ret = write_ldt(ptr, bytecount, 0); break; } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)7584.27%571.43%
Linus Torvalds1415.73%228.57%
Total89100.00%7100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
Andrew Lutomirski42734.86%12.17%
Linus Torvalds (pre-git)32926.86%1532.61%
Dave Jones23118.86%12.17%
Linus Torvalds584.73%24.35%
Thomas Gleixner393.18%24.35%
H. Peter Anvin221.80%12.17%
Glauber de Oliveira Costa211.71%24.35%
Jeremy Fitzhardinge191.55%24.35%
Jesper Juhl151.22%12.17%
Jan Beulich131.06%36.52%
Zachary Amsden110.90%12.17%
Ingo Molnar80.65%24.35%
Luiz Fernando N. Capitulino60.49%12.17%
Andrew Morton50.41%24.35%
Jaswinder Singh Rajput40.33%24.35%
Tejun Heo30.24%12.17%
Adrian Bunk30.24%12.17%
Rusty Russell30.24%12.17%
Manfred Spraul20.16%12.17%
Dave Hansen20.16%12.17%
Cyrill V. Gorcunov20.16%12.17%
Andrey Ryabinin10.08%12.17%
Dan Carpenter10.08%12.17%
Total1225100.00%46100.00%
Directory: arch/x86/kernel
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.