static void set_tls_desc(struct task_struct *p, int idx, const struct user_desc *info, int n) { struct thread_struct *t = &p->thread; struct desc_struct *desc = &t->tls_array[idx - GDT_ENTRY_TLS_MIN]; int cpu; /* * We must not get preempted while modifying the TLS. */ cpu = get_cpu(); while (n-- > 0) { if (LDT_empty(info)) desc->a = desc->b = 0; else fill_ldt(desc, info); ++info; ++desc; } if (t == ¤t->thread) load_TLS(t, cpu); put_cpu(); }
int set_tls_array(struct thread_struct *t, int idx, unsigned long addr, unsigned int limit) { struct user_desc info; struct desc_struct *desc; if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) return -EINVAL; desc = t->tls_array + idx - GDT_ENTRY_TLS_MIN; info.entry_number = idx; info.base_addr = addr; info.limit = limit; info.contents = 0; info.read_exec_only = 0; info.seg_not_present = 0; info.seg_32bit = 1; info.limit_in_pages = 1; info.useable = 0; /* FIXME */ fill_ldt(desc, &info); #if 0 desc->a = LDT_entry_a(&info); desc->b = LDT_entry_b(&info); #endif return 0; }
static int write_ldt(void __user *ptr, unsigned long bytecount, int oldmode) { struct mm_struct *mm = current->mm; struct desc_struct ldt; int error; struct user_desc ldt_info; int oldsize, newsize; struct ldt_struct *new_ldt, *old_ldt; 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 { #ifdef CONFIG_PAX_SEGMEXEC if ((mm->pax_flags & MF_PAX_SEGMEXEC) && (ldt_info.contents & MODIFY_LDT_CONTENTS_CODE)) { error = -EINVAL; goto out; } #endif 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((int)(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; }