| 1 | // SPDX-License-Identifier: GPL-2.0 | 
|---|
| 2 | /* | 
|---|
| 3 | * sys_ia32.c: Conversion between 32bit and 64bit native syscalls. Based on | 
|---|
| 4 | *             sys_sparc32 | 
|---|
| 5 | * | 
|---|
| 6 | * Copyright (C) 2000		VA Linux Co | 
|---|
| 7 | * Copyright (C) 2000		Don Dugger <n0ano@valinux.com> | 
|---|
| 8 | * Copyright (C) 1999		Arun Sharma <arun.sharma@intel.com> | 
|---|
| 9 | * Copyright (C) 1997,1998	Jakub Jelinek (jj@sunsite.mff.cuni.cz) | 
|---|
| 10 | * Copyright (C) 1997		David S. Miller (davem@caip.rutgers.edu) | 
|---|
| 11 | * Copyright (C) 2000		Hewlett-Packard Co. | 
|---|
| 12 | * Copyright (C) 2000		David Mosberger-Tang <davidm@hpl.hp.com> | 
|---|
| 13 | * Copyright (C) 2000,2001,2002	Andi Kleen, SuSE Labs (x86-64 port) | 
|---|
| 14 | * | 
|---|
| 15 | * These routines maintain argument size conversion between 32bit and 64bit | 
|---|
| 16 | * environment. In 2.5 most of this should be moved to a generic directory. | 
|---|
| 17 | * | 
|---|
| 18 | * This file assumes that there is a hole at the end of user address space. | 
|---|
| 19 | * | 
|---|
| 20 | * Some of the functions are LE specific currently. These are | 
|---|
| 21 | * hopefully all marked.  This should be fixed. | 
|---|
| 22 | */ | 
|---|
| 23 |  | 
|---|
| 24 | #include <linux/kernel.h> | 
|---|
| 25 | #include <linux/sched.h> | 
|---|
| 26 | #include <linux/fs.h> | 
|---|
| 27 | #include <linux/file.h> | 
|---|
| 28 | #include <linux/signal.h> | 
|---|
| 29 | #include <linux/syscalls.h> | 
|---|
| 30 | #include <linux/times.h> | 
|---|
| 31 | #include <linux/utsname.h> | 
|---|
| 32 | #include <linux/mm.h> | 
|---|
| 33 | #include <linux/uio.h> | 
|---|
| 34 | #include <linux/poll.h> | 
|---|
| 35 | #include <linux/personality.h> | 
|---|
| 36 | #include <linux/stat.h> | 
|---|
| 37 | #include <linux/rwsem.h> | 
|---|
| 38 | #include <linux/compat.h> | 
|---|
| 39 | #include <linux/vfs.h> | 
|---|
| 40 | #include <linux/ptrace.h> | 
|---|
| 41 | #include <linux/highuid.h> | 
|---|
| 42 | #include <linux/sysctl.h> | 
|---|
| 43 | #include <linux/slab.h> | 
|---|
| 44 | #include <linux/sched/task.h> | 
|---|
| 45 | #include <asm/mman.h> | 
|---|
| 46 | #include <asm/types.h> | 
|---|
| 47 | #include <linux/uaccess.h> | 
|---|
| 48 | #include <linux/atomic.h> | 
|---|
| 49 | #include <asm/vgtod.h> | 
|---|
| 50 | #include <asm/ia32.h> | 
|---|
| 51 |  | 
|---|
| 52 | #define AA(__x)		((unsigned long)(__x)) | 
|---|
| 53 |  | 
|---|
| 54 | SYSCALL_DEFINE3(ia32_truncate64, const char __user *, filename, | 
|---|
| 55 | unsigned long, offset_low, unsigned long, offset_high) | 
|---|
| 56 | { | 
|---|
| 57 | return ksys_truncate(pathname: filename, | 
|---|
| 58 | length: ((loff_t) offset_high << 32) | offset_low); | 
|---|
| 59 | } | 
|---|
| 60 |  | 
|---|
| 61 | SYSCALL_DEFINE3(ia32_ftruncate64, unsigned int, fd, | 
|---|
| 62 | unsigned long, offset_low, unsigned long, offset_high) | 
|---|
| 63 | { | 
|---|
| 64 | return ksys_ftruncate(fd, length: ((loff_t) offset_high << 32) | offset_low); | 
|---|
| 65 | } | 
|---|
| 66 |  | 
|---|
| 67 | /* warning: next two assume little endian */ | 
|---|
| 68 | SYSCALL_DEFINE5(ia32_pread64, unsigned int, fd, char __user *, ubuf, | 
|---|
| 69 | u32, count, u32, poslo, u32, poshi) | 
|---|
| 70 | { | 
|---|
| 71 | return ksys_pread64(fd, buf: ubuf, count, | 
|---|
| 72 | pos: ((loff_t)AA(poshi) << 32) | AA(poslo)); | 
|---|
| 73 | } | 
|---|
| 74 |  | 
|---|
| 75 | SYSCALL_DEFINE5(ia32_pwrite64, unsigned int, fd, const char __user *, ubuf, | 
|---|
| 76 | u32, count, u32, poslo, u32, poshi) | 
|---|
| 77 | { | 
|---|
| 78 | return ksys_pwrite64(fd, buf: ubuf, count, | 
|---|
| 79 | pos: ((loff_t)AA(poshi) << 32) | AA(poslo)); | 
|---|
| 80 | } | 
|---|
| 81 |  | 
|---|
| 82 |  | 
|---|
| 83 | /* | 
|---|
| 84 | * Some system calls that need sign extended arguments. This could be | 
|---|
| 85 | * done by a generic wrapper. | 
|---|
| 86 | */ | 
|---|
| 87 | SYSCALL_DEFINE6(ia32_fadvise64_64, int, fd, __u32, offset_low, | 
|---|
| 88 | __u32, offset_high, __u32, len_low, __u32, len_high, | 
|---|
| 89 | int, advice) | 
|---|
| 90 | { | 
|---|
| 91 | return ksys_fadvise64_64(fd, | 
|---|
| 92 | offset: (((u64)offset_high)<<32) | offset_low, | 
|---|
| 93 | len: (((u64)len_high)<<32) | len_low, | 
|---|
| 94 | advice); | 
|---|
| 95 | } | 
|---|
| 96 |  | 
|---|
| 97 | SYSCALL_DEFINE4(ia32_readahead, int, fd, unsigned int, off_lo, | 
|---|
| 98 | unsigned int, off_hi, size_t, count) | 
|---|
| 99 | { | 
|---|
| 100 | return ksys_readahead(fd, offset: ((u64)off_hi << 32) | off_lo, count); | 
|---|
| 101 | } | 
|---|
| 102 |  | 
|---|
| 103 | SYSCALL_DEFINE6(ia32_sync_file_range, int, fd, unsigned int, off_low, | 
|---|
| 104 | unsigned int, off_hi, unsigned int, n_low, | 
|---|
| 105 | unsigned int, n_hi, int, flags) | 
|---|
| 106 | { | 
|---|
| 107 | return ksys_sync_file_range(fd, | 
|---|
| 108 | offset: ((u64)off_hi << 32) | off_low, | 
|---|
| 109 | nbytes: ((u64)n_hi << 32) | n_low, flags); | 
|---|
| 110 | } | 
|---|
| 111 |  | 
|---|
| 112 | SYSCALL_DEFINE5(ia32_fadvise64, int, fd, unsigned int, offset_lo, | 
|---|
| 113 | unsigned int, offset_hi, size_t, len, int, advice) | 
|---|
| 114 | { | 
|---|
| 115 | return ksys_fadvise64_64(fd, offset: ((u64)offset_hi << 32) | offset_lo, | 
|---|
| 116 | len, advice); | 
|---|
| 117 | } | 
|---|
| 118 |  | 
|---|
| 119 | SYSCALL_DEFINE6(ia32_fallocate, int, fd, int, mode, | 
|---|
| 120 | unsigned int, offset_lo, unsigned int, offset_hi, | 
|---|
| 121 | unsigned int, len_lo, unsigned int, len_hi) | 
|---|
| 122 | { | 
|---|
| 123 | return ksys_fallocate(fd, mode, offset: ((u64)offset_hi << 32) | offset_lo, | 
|---|
| 124 | len: ((u64)len_hi << 32) | len_lo); | 
|---|
| 125 | } | 
|---|
| 126 |  | 
|---|
| 127 | #ifdef CONFIG_IA32_EMULATION | 
|---|
| 128 | /* | 
|---|
| 129 | * Another set for IA32/LFS -- x86_64 struct stat is different due to | 
|---|
| 130 | * support for 64bit inode numbers. | 
|---|
| 131 | */ | 
|---|
| 132 | static int cp_stat64(struct stat64 __user *ubuf, struct kstat *stat) | 
|---|
| 133 | { | 
|---|
| 134 | typeof(ubuf->st_uid) uid = 0; | 
|---|
| 135 | typeof(ubuf->st_gid) gid = 0; | 
|---|
| 136 | SET_UID(uid, from_kuid_munged(current_user_ns(), stat->uid)); | 
|---|
| 137 | SET_GID(gid, from_kgid_munged(current_user_ns(), stat->gid)); | 
|---|
| 138 | if (!user_write_access_begin(ubuf, sizeof(struct stat64))) | 
|---|
| 139 | return -EFAULT; | 
|---|
| 140 | unsafe_put_user(huge_encode_dev(stat->dev), &ubuf->st_dev, Efault); | 
|---|
| 141 | unsafe_put_user(stat->ino, &ubuf->__st_ino, Efault); | 
|---|
| 142 | unsafe_put_user(stat->ino, &ubuf->st_ino, Efault); | 
|---|
| 143 | unsafe_put_user(stat->mode, &ubuf->st_mode, Efault); | 
|---|
| 144 | unsafe_put_user(stat->nlink, &ubuf->st_nlink, Efault); | 
|---|
| 145 | unsafe_put_user(uid, &ubuf->st_uid, Efault); | 
|---|
| 146 | unsafe_put_user(gid, &ubuf->st_gid, Efault); | 
|---|
| 147 | unsafe_put_user(huge_encode_dev(stat->rdev), &ubuf->st_rdev, Efault); | 
|---|
| 148 | unsafe_put_user(stat->size, &ubuf->st_size, Efault); | 
|---|
| 149 | unsafe_put_user(stat->atime.tv_sec, &ubuf->st_atime, Efault); | 
|---|
| 150 | unsafe_put_user(stat->atime.tv_nsec, &ubuf->st_atime_nsec, Efault); | 
|---|
| 151 | unsafe_put_user(stat->mtime.tv_sec, &ubuf->st_mtime, Efault); | 
|---|
| 152 | unsafe_put_user(stat->mtime.tv_nsec, &ubuf->st_mtime_nsec, Efault); | 
|---|
| 153 | unsafe_put_user(stat->ctime.tv_sec, &ubuf->st_ctime, Efault); | 
|---|
| 154 | unsafe_put_user(stat->ctime.tv_nsec, &ubuf->st_ctime_nsec, Efault); | 
|---|
| 155 | unsafe_put_user(stat->blksize, &ubuf->st_blksize, Efault); | 
|---|
| 156 | unsafe_put_user(stat->blocks, &ubuf->st_blocks, Efault); | 
|---|
| 157 | user_access_end(); | 
|---|
| 158 | return 0; | 
|---|
| 159 | Efault: | 
|---|
| 160 | user_write_access_end(); | 
|---|
| 161 | return -EFAULT; | 
|---|
| 162 | } | 
|---|
| 163 |  | 
|---|
| 164 | COMPAT_SYSCALL_DEFINE2(ia32_stat64, const char __user *, filename, | 
|---|
| 165 | struct stat64 __user *, statbuf) | 
|---|
| 166 | { | 
|---|
| 167 | struct kstat stat; | 
|---|
| 168 | int ret = vfs_stat(filename, stat: &stat); | 
|---|
| 169 |  | 
|---|
| 170 | if (!ret) | 
|---|
| 171 | ret = cp_stat64(ubuf: statbuf, stat: &stat); | 
|---|
| 172 | return ret; | 
|---|
| 173 | } | 
|---|
| 174 |  | 
|---|
| 175 | COMPAT_SYSCALL_DEFINE2(ia32_lstat64, const char __user *, filename, | 
|---|
| 176 | struct stat64 __user *, statbuf) | 
|---|
| 177 | { | 
|---|
| 178 | struct kstat stat; | 
|---|
| 179 | int ret = vfs_lstat(name: filename, stat: &stat); | 
|---|
| 180 | if (!ret) | 
|---|
| 181 | ret = cp_stat64(ubuf: statbuf, stat: &stat); | 
|---|
| 182 | return ret; | 
|---|
| 183 | } | 
|---|
| 184 |  | 
|---|
| 185 | COMPAT_SYSCALL_DEFINE2(ia32_fstat64, unsigned int, fd, | 
|---|
| 186 | struct stat64 __user *, statbuf) | 
|---|
| 187 | { | 
|---|
| 188 | struct kstat stat; | 
|---|
| 189 | int ret = vfs_fstat(fd, stat: &stat); | 
|---|
| 190 | if (!ret) | 
|---|
| 191 | ret = cp_stat64(ubuf: statbuf, stat: &stat); | 
|---|
| 192 | return ret; | 
|---|
| 193 | } | 
|---|
| 194 |  | 
|---|
| 195 | COMPAT_SYSCALL_DEFINE4(ia32_fstatat64, unsigned int, dfd, | 
|---|
| 196 | const char __user *, filename, | 
|---|
| 197 | struct stat64 __user *, statbuf, int, flag) | 
|---|
| 198 | { | 
|---|
| 199 | struct kstat stat; | 
|---|
| 200 | int error; | 
|---|
| 201 |  | 
|---|
| 202 | error = vfs_fstatat(dfd, filename, stat: &stat, flags: flag); | 
|---|
| 203 | if (error) | 
|---|
| 204 | return error; | 
|---|
| 205 | return cp_stat64(ubuf: statbuf, stat: &stat); | 
|---|
| 206 | } | 
|---|
| 207 |  | 
|---|
| 208 | /* | 
|---|
| 209 | * Linux/i386 didn't use to be able to handle more than | 
|---|
| 210 | * 4 system call parameters, so these system calls used a memory | 
|---|
| 211 | * block for parameter passing.. | 
|---|
| 212 | */ | 
|---|
| 213 |  | 
|---|
| 214 | struct mmap_arg_struct32 { | 
|---|
| 215 | unsigned int addr; | 
|---|
| 216 | unsigned int len; | 
|---|
| 217 | unsigned int prot; | 
|---|
| 218 | unsigned int flags; | 
|---|
| 219 | unsigned int fd; | 
|---|
| 220 | unsigned int offset; | 
|---|
| 221 | }; | 
|---|
| 222 |  | 
|---|
| 223 | COMPAT_SYSCALL_DEFINE1(ia32_mmap, struct mmap_arg_struct32 __user *, arg) | 
|---|
| 224 | { | 
|---|
| 225 | struct mmap_arg_struct32 a; | 
|---|
| 226 |  | 
|---|
| 227 | if (copy_from_user(to: &a, from: arg, n: sizeof(a))) | 
|---|
| 228 | return -EFAULT; | 
|---|
| 229 |  | 
|---|
| 230 | if (a.offset & ~PAGE_MASK) | 
|---|
| 231 | return -EINVAL; | 
|---|
| 232 |  | 
|---|
| 233 | return ksys_mmap_pgoff(addr: a.addr, len: a.len, prot: a.prot, flags: a.flags, fd: a.fd, | 
|---|
| 234 | pgoff: a.offset>>PAGE_SHIFT); | 
|---|
| 235 | } | 
|---|
| 236 |  | 
|---|
| 237 | /* | 
|---|
| 238 | * The 32-bit clone ABI is CONFIG_CLONE_BACKWARDS | 
|---|
| 239 | */ | 
|---|
| 240 | COMPAT_SYSCALL_DEFINE5(ia32_clone, unsigned long, clone_flags, | 
|---|
| 241 | unsigned long, newsp, int __user *, parent_tidptr, | 
|---|
| 242 | unsigned long, tls_val, int __user *, child_tidptr) | 
|---|
| 243 | { | 
|---|
| 244 | struct kernel_clone_args args = { | 
|---|
| 245 | .flags		= (clone_flags & ~CSIGNAL), | 
|---|
| 246 | .pidfd		= parent_tidptr, | 
|---|
| 247 | .child_tid	= child_tidptr, | 
|---|
| 248 | .parent_tid	= parent_tidptr, | 
|---|
| 249 | .exit_signal	= (clone_flags & CSIGNAL), | 
|---|
| 250 | .stack		= newsp, | 
|---|
| 251 | .tls		= tls_val, | 
|---|
| 252 | }; | 
|---|
| 253 |  | 
|---|
| 254 | return kernel_clone(kargs: &args); | 
|---|
| 255 | } | 
|---|
| 256 | #endif /* CONFIG_IA32_EMULATION */ | 
|---|
| 257 |  | 
|---|