linux-old/arch/i386/kernel/ldt.c
<<
>>
Prefs
   1/*
   2 * linux/kernel/ldt.c
   3 *
   4 * Copyright (C) 1992 Krishna Balasubramanian and Linus Torvalds
   5 * Copyright (C) 1999 Ingo Molnar <mingo@redhat.com>
   6 */
   7
   8#include <linux/errno.h>
   9#include <linux/sched.h>
  10#include <linux/string.h>
  11#include <linux/mm.h>
  12#include <linux/smp.h>
  13#include <linux/smp_lock.h>
  14#include <linux/vmalloc.h>
  15
  16#include <asm/uaccess.h>
  17#include <asm/system.h>
  18#include <asm/ldt.h>
  19#include <asm/desc.h>
  20
  21/*
  22 * read_ldt() is not really atomic - this is not a problem since
  23 * synchronization of reads and writes done to the LDT has to be
  24 * assured by user-space anyway. Writes are atomic, to protect
  25 * the security checks done on new descriptors.
  26 */
  27static int read_ldt(void * ptr, unsigned long bytecount)
  28{
  29        int err;
  30        unsigned long size;
  31        struct mm_struct * mm = current->mm;
  32
  33        err = 0;
  34        if (!mm->context.segments)
  35                goto out;
  36
  37        size = LDT_ENTRIES*LDT_ENTRY_SIZE;
  38        if (size > bytecount)
  39                size = bytecount;
  40
  41        err = size;
  42        if (copy_to_user(ptr, mm->context.segments, size))
  43                err = -EFAULT;
  44out:
  45        return err;
  46}
  47
  48static int read_default_ldt(void * ptr, unsigned long bytecount)
  49{
  50        int err;
  51        unsigned long size;
  52        void *address;
  53
  54        err = 0;
  55        address = &default_ldt[0];
  56        size = sizeof(struct desc_struct);
  57        if (size > bytecount)
  58                size = bytecount;
  59
  60        err = size;
  61        if (copy_to_user(ptr, address, size))
  62                err = -EFAULT;
  63
  64        return err;
  65}
  66
  67static int write_ldt(void * ptr, unsigned long bytecount, int oldmode)
  68{
  69        struct mm_struct * mm = current->mm;
  70        __u32 entry_1, entry_2, *lp;
  71        int error;
  72        struct modify_ldt_ldt_s ldt_info;
  73
  74        error = -EINVAL;
  75        if (bytecount != sizeof(ldt_info))
  76                goto out;
  77        error = -EFAULT;        
  78        if (copy_from_user(&ldt_info, ptr, sizeof(ldt_info)))
  79                goto out;
  80
  81        error = -EINVAL;
  82        if (ldt_info.entry_number >= LDT_ENTRIES)
  83                goto out;
  84        if (ldt_info.contents == 3) {
  85                if (oldmode)
  86                        goto out;
  87                if (ldt_info.seg_not_present == 0)
  88                        goto out;
  89        }
  90
  91        /*
  92         * the GDT index of the LDT is allocated dynamically, and is
  93         * limited by MAX_LDT_DESCRIPTORS.
  94         */
  95        down_write(&mm->mmap_sem);
  96        if (!mm->context.segments) {
  97                void * segments = vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE);
  98                error = -ENOMEM;
  99                if (!segments)
 100                        goto out_unlock;
 101                memset(segments, 0, LDT_ENTRIES*LDT_ENTRY_SIZE);
 102                wmb();
 103                mm->context.segments = segments;
 104                mm->context.cpuvalid = 1UL << smp_processor_id();
 105                load_LDT(mm);
 106        }
 107
 108        lp = (__u32 *) ((ldt_info.entry_number << 3) + (char *) mm->context.segments);
 109
 110        /* Allow LDTs to be cleared by the user. */
 111        if (ldt_info.base_addr == 0 && ldt_info.limit == 0) {
 112                if (oldmode ||
 113                    (ldt_info.contents == 0             &&
 114                     ldt_info.read_exec_only == 1       &&
 115                     ldt_info.seg_32bit == 0            &&
 116                     ldt_info.limit_in_pages == 0       &&
 117                     ldt_info.seg_not_present == 1      &&
 118                     ldt_info.useable == 0 )) {
 119                        entry_1 = 0;
 120                        entry_2 = 0;
 121                        goto install;
 122                }
 123        }
 124
 125        entry_1 = ((ldt_info.base_addr & 0x0000ffff) << 16) |
 126                  (ldt_info.limit & 0x0ffff);
 127        entry_2 = (ldt_info.base_addr & 0xff000000) |
 128                  ((ldt_info.base_addr & 0x00ff0000) >> 16) |
 129                  (ldt_info.limit & 0xf0000) |
 130                  ((ldt_info.read_exec_only ^ 1) << 9) |
 131                  (ldt_info.contents << 10) |
 132                  ((ldt_info.seg_not_present ^ 1) << 15) |
 133                  (ldt_info.seg_32bit << 22) |
 134                  (ldt_info.limit_in_pages << 23) |
 135                  0x7000;
 136        if (!oldmode)
 137                entry_2 |= (ldt_info.useable << 20);
 138
 139        /* Install the new entry ...  */
 140install:
 141        *lp     = entry_1;
 142        *(lp+1) = entry_2;
 143        error = 0;
 144
 145out_unlock:
 146        up_write(&mm->mmap_sem);
 147out:
 148        return error;
 149}
 150
 151asmlinkage int sys_modify_ldt(int func, void *ptr, unsigned long bytecount)
 152{
 153        int ret = -ENOSYS;
 154
 155        switch (func) {
 156        case 0:
 157                ret = read_ldt(ptr, bytecount);
 158                break;
 159        case 1:
 160                ret = write_ldt(ptr, bytecount, 1);
 161                break;
 162        case 2:
 163                ret = read_default_ldt(ptr, bytecount);
 164                break;
 165        case 0x11:
 166                ret = write_ldt(ptr, bytecount, 0);
 167                break;
 168        }
 169        return ret;
 170}
 171
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.