Skip to content

Commit

Permalink
[PATCH] lightweight robust futexes: compat
Browse files Browse the repository at this point in the history
32-bit syscall compatibility support.  (This patch also moves all futex
related compat functionality into kernel/futex_compat.c.)

Signed-off-by: Ingo Molnar <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
Signed-off-by: Arjan van de Ven <[email protected]>
Acked-by: Ulrich Drepper <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Ingo Molnar authored and Linus Torvalds committed Mar 27, 2006
1 parent 2eec9ad commit 34f192c
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 23 deletions.
18 changes: 18 additions & 0 deletions include/linux/compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,24 @@ typedef struct compat_sigevent {
} _sigev_un;
} compat_sigevent_t;

struct compat_robust_list {
compat_uptr_t next;
};

struct compat_robust_list_head {
struct compat_robust_list list;
compat_long_t futex_offset;
compat_uptr_t list_op_pending;
};

extern void compat_exit_robust_list(struct task_struct *curr);

asmlinkage long
compat_sys_set_robust_list(struct compat_robust_list_head __user *head,
compat_size_t len);
asmlinkage long
compat_sys_get_robust_list(int pid, compat_uptr_t *head_ptr,
compat_size_t __user *len_ptr);

long compat_sys_semctl(int first, int second, int third, void __user *uptr);
long compat_sys_msgsnd(int first, int second, int third, void __user *uptr);
Expand Down
3 changes: 3 additions & 0 deletions include/linux/sched.h
Original file line number Diff line number Diff line change
Expand Up @@ -874,6 +874,9 @@ struct task_struct {
int cpuset_mem_spread_rotor;
#endif
struct robust_list_head __user *robust_list;
#ifdef CONFIG_COMPAT
struct compat_robust_list_head __user *compat_robust_list;
#endif

atomic_t fs_excl; /* holding fs exclusive resources */
struct rcu_head rcu;
Expand Down
3 changes: 3 additions & 0 deletions kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \

obj-$(CONFIG_DEBUG_MUTEXES) += mutex-debug.o
obj-$(CONFIG_FUTEX) += futex.o
ifeq ($(CONFIG_COMPAT),y)
obj-$(CONFIG_FUTEX) += futex_compat.o
endif
obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o
obj-$(CONFIG_SMP) += cpu.o spinlock.o
obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o
Expand Down
23 changes: 0 additions & 23 deletions kernel/compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
#include <linux/time.h>
#include <linux/signal.h>
#include <linux/sched.h> /* for MAX_SCHEDULE_TIMEOUT */
#include <linux/futex.h> /* for FUTEX_WAIT */
#include <linux/syscalls.h>
#include <linux/unistd.h>
#include <linux/security.h>
Expand Down Expand Up @@ -239,28 +238,6 @@ asmlinkage long compat_sys_sigprocmask(int how, compat_old_sigset_t __user *set,
return ret;
}

#ifdef CONFIG_FUTEX
asmlinkage long compat_sys_futex(u32 __user *uaddr, int op, int val,
struct compat_timespec __user *utime, u32 __user *uaddr2,
int val3)
{
struct timespec t;
unsigned long timeout = MAX_SCHEDULE_TIMEOUT;
int val2 = 0;

if ((op == FUTEX_WAIT) && utime) {
if (get_compat_timespec(&t, utime))
return -EFAULT;
timeout = timespec_to_jiffies(&t) + 1;
}
if (op >= FUTEX_REQUEUE)
val2 = (int) (unsigned long) utime;

return do_futex((unsigned long)uaddr, op, val, timeout,
(unsigned long)uaddr2, val2, val3);
}
#endif

asmlinkage long compat_sys_setrlimit(unsigned int resource,
struct compat_rlimit __user *rlim)
{
Expand Down
5 changes: 5 additions & 0 deletions kernel/exit.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <linux/cn_proc.h>
#include <linux/mutex.h>
#include <linux/futex.h>
#include <linux/compat.h>

#include <asm/uaccess.h>
#include <asm/unistd.h>
Expand Down Expand Up @@ -855,6 +856,10 @@ fastcall NORET_TYPE void do_exit(long code)
}
if (unlikely(tsk->robust_list))
exit_robust_list(tsk);
#ifdef CONFIG_COMPAT
if (unlikely(tsk->compat_robust_list))
compat_exit_robust_list(tsk);
#endif
exit_mm(tsk);

exit_sem(tsk);
Expand Down
142 changes: 142 additions & 0 deletions kernel/futex_compat.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* linux/kernel/futex_compat.c
*
* Futex compatibililty routines.
*
* Copyright 2006, Red Hat, Inc., Ingo Molnar
*/

#include <linux/linkage.h>
#include <linux/compat.h>
#include <linux/futex.h>

#include <asm/uaccess.h>

/*
* Walk curr->robust_list (very carefully, it's a userspace list!)
* and mark any locks found there dead, and notify any waiters.
*
* We silently return on any sign of list-walking problem.
*/
void compat_exit_robust_list(struct task_struct *curr)
{
struct compat_robust_list_head __user *head = curr->compat_robust_list;
struct robust_list __user *entry, *pending;
compat_uptr_t uentry, upending;
unsigned int limit = ROBUST_LIST_LIMIT;
compat_long_t futex_offset;

/*
* Fetch the list head (which was registered earlier, via
* sys_set_robust_list()):
*/
if (get_user(uentry, &head->list.next))
return;
entry = compat_ptr(uentry);
/*
* Fetch the relative futex offset:
*/
if (get_user(futex_offset, &head->futex_offset))
return;
/*
* Fetch any possibly pending lock-add first, and handle it
* if it exists:
*/
if (get_user(upending, &head->list_op_pending))
return;
pending = compat_ptr(upending);
if (upending)
handle_futex_death((void *)pending + futex_offset, curr);

while (compat_ptr(uentry) != &head->list) {
/*
* A pending lock might already be on the list, so
* dont process it twice:
*/
if (entry != pending)
if (handle_futex_death((void *)entry + futex_offset,
curr))
return;

/*
* Fetch the next entry in the list:
*/
if (get_user(uentry, (compat_uptr_t *)&entry->next))
return;
entry = compat_ptr(uentry);
/*
* Avoid excessively long or circular lists:
*/
if (!--limit)
break;

cond_resched();
}
}

asmlinkage long
compat_sys_set_robust_list(struct compat_robust_list_head __user *head,
compat_size_t len)
{
if (unlikely(len != sizeof(*head)))
return -EINVAL;

current->compat_robust_list = head;

return 0;
}

asmlinkage long
compat_sys_get_robust_list(int pid, compat_uptr_t *head_ptr,
compat_size_t __user *len_ptr)
{
struct compat_robust_list_head *head;
unsigned long ret;

if (!pid)
head = current->compat_robust_list;
else {
struct task_struct *p;

ret = -ESRCH;
read_lock(&tasklist_lock);
p = find_task_by_pid(pid);
if (!p)
goto err_unlock;
ret = -EPERM;
if ((current->euid != p->euid) && (current->euid != p->uid) &&
!capable(CAP_SYS_PTRACE))
goto err_unlock;
head = p->compat_robust_list;
read_unlock(&tasklist_lock);
}

if (put_user(sizeof(*head), len_ptr))
return -EFAULT;
return put_user(ptr_to_compat(head), head_ptr);

err_unlock:
read_unlock(&tasklist_lock);

return ret;
}

asmlinkage long compat_sys_futex(u32 __user *uaddr, int op, int val,
struct compat_timespec __user *utime, u32 __user *uaddr2,
int val3)
{
struct timespec t;
unsigned long timeout = MAX_SCHEDULE_TIMEOUT;
int val2 = 0;

if ((op == FUTEX_WAIT) && utime) {
if (get_compat_timespec(&t, utime))
return -EFAULT;
timeout = timespec_to_jiffies(&t) + 1;
}
if (op >= FUTEX_REQUEUE)
val2 = (int) (unsigned long) utime;

return do_futex((unsigned long)uaddr, op, val, timeout,
(unsigned long)uaddr2, val2, val3);
}

0 comments on commit 34f192c

Please sign in to comment.