Blame view

kernel/compat.c 7.31 KB
d2912cb15   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
5
6
7
8
  /*
   *  linux/kernel/compat.c
   *
   *  Kernel compatibililty routines for e.g. 32 bit syscall support
   *  on 64 bit kernels.
   *
   *  Copyright (C) 2002-2003 Stephen Rothwell, IBM Corporation
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9
10
11
12
13
14
15
16
   */
  
  #include <linux/linkage.h>
  #include <linux/compat.h>
  #include <linux/errno.h>
  #include <linux/time.h>
  #include <linux/signal.h>
  #include <linux/sched.h>	/* for MAX_SCHEDULE_TIMEOUT */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
  #include <linux/syscalls.h>
  #include <linux/unistd.h>
  #include <linux/security.h>
6e5fdeedc   Paul Gortmaker   kernel: Fix files...
20
  #include <linux/export.h>
1b2db9fb7   Christoph Lameter   [PATCH] sys_move_...
21
  #include <linux/migrate.h>
1711ef386   Toyo Abe   [PATCH] posix-tim...
22
  #include <linux/posix-timers.h>
f06febc96   Frank Mayhar   timers: fix itime...
23
  #include <linux/times.h>
e3d5a27d5   Paul Mackerras   Allow times and t...
24
  #include <linux/ptrace.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
25
  #include <linux/gfp.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26

7c0f6ba68   Linus Torvalds   Replace <asm/uacc...
27
  #include <linux/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28

be84cb438   Chris Metcalf   compat: fixes to ...
29
  #ifdef __ARCH_WANT_SYS_SIGPROCMASK
b7dafa0ef   Jan Kiszka   compat: Fix RT si...
30
31
32
33
34
  /*
   * sys_sigprocmask SIG_SETMASK sets the first (compat) word of the
   * blocked set of signals to the supplied signal set
   */
  static inline void compat_sig_setmask(sigset_t *blocked, compat_sigset_word set)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35
  {
b7dafa0ef   Jan Kiszka   compat: Fix RT si...
36
37
  	memcpy(blocked->sig, &set, sizeof(set));
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38

5cf221002   Al Viro   switch compat_sys...
39
40
41
  COMPAT_SYSCALL_DEFINE3(sigprocmask, int, how,
  		       compat_old_sigset_t __user *, nset,
  		       compat_old_sigset_t __user *, oset)
b7dafa0ef   Jan Kiszka   compat: Fix RT si...
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
  {
  	old_sigset_t old_set, new_set;
  	sigset_t new_blocked;
  
  	old_set = current->blocked.sig[0];
  
  	if (nset) {
  		if (get_user(new_set, nset))
  			return -EFAULT;
  		new_set &= ~(sigmask(SIGKILL) | sigmask(SIGSTOP));
  
  		new_blocked = current->blocked;
  
  		switch (how) {
  		case SIG_BLOCK:
  			sigaddsetmask(&new_blocked, new_set);
  			break;
  		case SIG_UNBLOCK:
  			sigdelsetmask(&new_blocked, new_set);
  			break;
  		case SIG_SETMASK:
  			compat_sig_setmask(&new_blocked, new_set);
  			break;
  		default:
  			return -EINVAL;
  		}
  
  		set_current_blocked(&new_blocked);
  	}
  
  	if (oset) {
  		if (put_user(old_set, oset))
  			return -EFAULT;
  	}
  
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78
  }
be84cb438   Chris Metcalf   compat: fixes to ...
79
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
81
  int put_compat_rusage(const struct rusage *r, struct compat_rusage __user *ru)
  {
7668b679c   Al Viro   put_compat_rusage...
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
  	struct compat_rusage r32;
  	memset(&r32, 0, sizeof(r32));
  	r32.ru_utime.tv_sec = r->ru_utime.tv_sec;
  	r32.ru_utime.tv_usec = r->ru_utime.tv_usec;
  	r32.ru_stime.tv_sec = r->ru_stime.tv_sec;
  	r32.ru_stime.tv_usec = r->ru_stime.tv_usec;
  	r32.ru_maxrss = r->ru_maxrss;
  	r32.ru_ixrss = r->ru_ixrss;
  	r32.ru_idrss = r->ru_idrss;
  	r32.ru_isrss = r->ru_isrss;
  	r32.ru_minflt = r->ru_minflt;
  	r32.ru_majflt = r->ru_majflt;
  	r32.ru_nswap = r->ru_nswap;
  	r32.ru_inblock = r->ru_inblock;
  	r32.ru_oublock = r->ru_oublock;
  	r32.ru_msgsnd = r->ru_msgsnd;
  	r32.ru_msgrcv = r->ru_msgrcv;
  	r32.ru_nsignals = r->ru_nsignals;
  	r32.ru_nvcsw = r->ru_nvcsw;
  	r32.ru_nivcsw = r->ru_nivcsw;
  	if (copy_to_user(ru, &r32, sizeof(r32)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
103
104
105
  		return -EFAULT;
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
  static int compat_get_user_cpu_mask(compat_ulong_t __user *user_mask_ptr,
a45185d2d   Rusty Russell   cpumask: convert ...
107
  				    unsigned len, struct cpumask *new_mask)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
109
  {
  	unsigned long *k;
a45185d2d   Rusty Russell   cpumask: convert ...
110
111
112
113
  	if (len < cpumask_size())
  		memset(new_mask, 0, cpumask_size());
  	else if (len > cpumask_size())
  		len = cpumask_size();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114

a45185d2d   Rusty Russell   cpumask: convert ...
115
  	k = cpumask_bits(new_mask);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
117
  	return compat_get_bitmap(k, user_mask_ptr, len * 8);
  }
62a6fa976   Heiko Carstens   kernel/compat: co...
118
119
120
  COMPAT_SYSCALL_DEFINE3(sched_setaffinity, compat_pid_t, pid,
  		       unsigned int, len,
  		       compat_ulong_t __user *, user_mask_ptr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
121
  {
a45185d2d   Rusty Russell   cpumask: convert ...
122
  	cpumask_var_t new_mask;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
  	int retval;
a45185d2d   Rusty Russell   cpumask: convert ...
124
125
126
127
  	if (!alloc_cpumask_var(&new_mask, GFP_KERNEL))
  		return -ENOMEM;
  
  	retval = compat_get_user_cpu_mask(user_mask_ptr, len, new_mask);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
  	if (retval)
a45185d2d   Rusty Russell   cpumask: convert ...
129
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130

a45185d2d   Rusty Russell   cpumask: convert ...
131
132
133
134
  	retval = sched_setaffinity(pid, new_mask);
  out:
  	free_cpumask_var(new_mask);
  	return retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
  }
62a6fa976   Heiko Carstens   kernel/compat: co...
136
137
  COMPAT_SYSCALL_DEFINE3(sched_getaffinity, compat_pid_t,  pid, unsigned int, len,
  		       compat_ulong_t __user *, user_mask_ptr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
139
  {
  	int ret;
a45185d2d   Rusty Russell   cpumask: convert ...
140
  	cpumask_var_t mask;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141

fa9dc265a   KOSAKI Motohiro   cpumask: fix comp...
142
143
144
  	if ((len * BITS_PER_BYTE) < nr_cpu_ids)
  		return -EINVAL;
  	if (len & (sizeof(compat_ulong_t)-1))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145
  		return -EINVAL;
a45185d2d   Rusty Russell   cpumask: convert ...
146
147
148
149
  	if (!alloc_cpumask_var(&mask, GFP_KERNEL))
  		return -ENOMEM;
  
  	ret = sched_getaffinity(pid, mask);
fa9dc265a   KOSAKI Motohiro   cpumask: fix comp...
150
  	if (ret == 0) {
4de373a12   Alexey Dobriyan   cpumask: make cpu...
151
  		unsigned int retlen = min(len, cpumask_size());
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152

fa9dc265a   KOSAKI Motohiro   cpumask: fix comp...
153
154
155
156
157
  		if (compat_put_bitmap(user_mask_ptr, cpumask_bits(mask), retlen * 8))
  			ret = -EFAULT;
  		else
  			ret = retlen;
  	}
a45185d2d   Rusty Russell   cpumask: convert ...
158
  	free_cpumask_var(mask);
fa9dc265a   KOSAKI Motohiro   cpumask: fix comp...
159

a45185d2d   Rusty Russell   cpumask: convert ...
160
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
161
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
163
164
165
166
167
168
169
170
171
  /*
   * We currently only need the following fields from the sigevent
   * structure: sigev_value, sigev_signo, sig_notify and (sometimes
   * sigev_notify_thread_id).  The others are handled in user mode.
   * We also assume that copying sigev_value.sival_int is sufficient
   * to keep all the bits of sigev_value.sival_ptr intact.
   */
  int get_compat_sigevent(struct sigevent *event,
  		const struct compat_sigevent __user *u_event)
  {
51410d3c5   David S. Miller   [PATCH] Fix get_c...
172
  	memset(event, 0, sizeof(*event));
96d4f267e   Linus Torvalds   Remove 'type' arg...
173
  	return (!access_ok(u_event, sizeof(*u_event)) ||
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174
175
176
177
178
179
180
181
  		__get_user(event->sigev_value.sival_int,
  			&u_event->sigev_value.sival_int) ||
  		__get_user(event->sigev_signo, &u_event->sigev_signo) ||
  		__get_user(event->sigev_notify, &u_event->sigev_notify) ||
  		__get_user(event->sigev_notify_thread_id,
  			&u_event->sigev_notify_thread_id))
  		? -EFAULT : 0;
  }
5fa3839a6   Stephen Rothwell   [PATCH] Constify ...
182
  long compat_get_bitmap(unsigned long *mask, const compat_ulong_t __user *umask,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
184
  		       unsigned long bitmap_size)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
186
187
188
  	unsigned long nr_compat_longs;
  
  	/* align bitmap up to nearest compat_long_t boundary */
  	bitmap_size = ALIGN(bitmap_size, BITS_PER_COMPAT_LONG);
1e1fc1334   Al Viro   compat_{get,put}_...
189
  	nr_compat_longs = BITS_TO_COMPAT_LONGS(bitmap_size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190

41cd78052   Christophe Leroy   uaccess: Selectiv...
191
  	if (!user_read_access_begin(umask, bitmap_size / 8))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
  		return -EFAULT;
1e1fc1334   Al Viro   compat_{get,put}_...
193
194
195
196
197
198
  	while (nr_compat_longs > 1) {
  		compat_ulong_t l1, l2;
  		unsafe_get_user(l1, umask++, Efault);
  		unsafe_get_user(l2, umask++, Efault);
  		*mask++ = ((unsigned long)l2 << BITS_PER_COMPAT_LONG) | l1;
  		nr_compat_longs -= 2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
  	}
1e1fc1334   Al Viro   compat_{get,put}_...
200
201
  	if (nr_compat_longs)
  		unsafe_get_user(*mask, umask++, Efault);
41cd78052   Christophe Leroy   uaccess: Selectiv...
202
  	user_read_access_end();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
  	return 0;
1e1fc1334   Al Viro   compat_{get,put}_...
204
205
  
  Efault:
41cd78052   Christophe Leroy   uaccess: Selectiv...
206
  	user_read_access_end();
1e1fc1334   Al Viro   compat_{get,put}_...
207
  	return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
209
210
211
212
  }
  
  long compat_put_bitmap(compat_ulong_t __user *umask, unsigned long *mask,
  		       unsigned long bitmap_size)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
213
214
215
216
  	unsigned long nr_compat_longs;
  
  	/* align bitmap up to nearest compat_long_t boundary */
  	bitmap_size = ALIGN(bitmap_size, BITS_PER_COMPAT_LONG);
1e1fc1334   Al Viro   compat_{get,put}_...
217
  	nr_compat_longs = BITS_TO_COMPAT_LONGS(bitmap_size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218

41cd78052   Christophe Leroy   uaccess: Selectiv...
219
  	if (!user_write_access_begin(umask, bitmap_size / 8))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
220
  		return -EFAULT;
1e1fc1334   Al Viro   compat_{get,put}_...
221
222
223
224
225
  	while (nr_compat_longs > 1) {
  		unsigned long m = *mask++;
  		unsafe_put_user((compat_ulong_t)m, umask++, Efault);
  		unsafe_put_user(m >> BITS_PER_COMPAT_LONG, umask++, Efault);
  		nr_compat_longs -= 2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
  	}
1e1fc1334   Al Viro   compat_{get,put}_...
227
228
  	if (nr_compat_longs)
  		unsafe_put_user((compat_ulong_t)*mask, umask++, Efault);
41cd78052   Christophe Leroy   uaccess: Selectiv...
229
  	user_write_access_end();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
230
  	return 0;
1e1fc1334   Al Viro   compat_{get,put}_...
231
  Efault:
41cd78052   Christophe Leroy   uaccess: Selectiv...
232
  	user_write_access_end();
1e1fc1334   Al Viro   compat_{get,put}_...
233
  	return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
234
  }
3968cf623   Al Viro   get_compat_sigset()
235
236
  int
  get_compat_sigset(sigset_t *set, const compat_sigset_t __user *compat)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237
  {
3968cf623   Al Viro   get_compat_sigset()
238
239
240
241
  #ifdef __BIG_ENDIAN
  	compat_sigset_t v;
  	if (copy_from_user(&v, compat, sizeof(compat_sigset_t)))
  		return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
242
  	switch (_NSIG_WORDS) {
3968cf623   Al Viro   get_compat_sigset()
243
  	case 4: set->sig[3] = v.sig[6] | (((long)v.sig[7]) << 32 );
df561f668   Gustavo A. R. Silva   treewide: Use fal...
244
  		fallthrough;
3968cf623   Al Viro   get_compat_sigset()
245
  	case 3: set->sig[2] = v.sig[4] | (((long)v.sig[5]) << 32 );
df561f668   Gustavo A. R. Silva   treewide: Use fal...
246
  		fallthrough;
3968cf623   Al Viro   get_compat_sigset()
247
  	case 2: set->sig[1] = v.sig[2] | (((long)v.sig[3]) << 32 );
df561f668   Gustavo A. R. Silva   treewide: Use fal...
248
  		fallthrough;
3968cf623   Al Viro   get_compat_sigset()
249
  	case 1: set->sig[0] = v.sig[0] | (((long)v.sig[1]) << 32 );
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
  	}
3968cf623   Al Viro   get_compat_sigset()
251
252
253
254
255
  #else
  	if (copy_from_user(set, compat, sizeof(compat_sigset_t)))
  		return -EFAULT;
  #endif
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256
  }
3968cf623   Al Viro   get_compat_sigset()
257
  EXPORT_SYMBOL_GPL(get_compat_sigset);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258

c41d68a51   H. Peter Anvin   compat: Make comp...
259
260
261
262
263
264
265
266
267
268
269
270
271
  /*
   * Allocate user-space memory for the duration of a single system call,
   * in order to marshall parameters inside a compat thunk.
   */
  void __user *compat_alloc_user_space(unsigned long len)
  {
  	void __user *ptr;
  
  	/* If len would occupy more than half of the entire compat space... */
  	if (unlikely(len > (((compat_uptr_t)~0) >> 1)))
  		return NULL;
  
  	ptr = arch_compat_alloc_user_space(len);
96d4f267e   Linus Torvalds   Remove 'type' arg...
272
  	if (unlikely(!access_ok(ptr, len)))
c41d68a51   H. Peter Anvin   compat: Make comp...
273
274
275
276
277
  		return NULL;
  
  	return ptr;
  }
  EXPORT_SYMBOL_GPL(compat_alloc_user_space);