Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <asm/unistd.h> | 5 #include <asm/unistd.h> |
| 6 #include <bits/wordsize.h> | 6 #include <bits/wordsize.h> |
| 7 #include <errno.h> | 7 #include <errno.h> |
| 8 #include <stdarg.h> | |
| 9 | 8 |
| 10 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" | 9 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" |
| 11 #include "sandbox/linux/seccomp-bpf/syscall.h" | 10 #include "sandbox/linux/seccomp-bpf/syscall.h" |
| 12 | 11 |
| 13 | 12 |
| 14 namespace playground2 { | 13 namespace playground2 { |
| 15 | 14 |
| 16 asm( // We need to be able to tell the kernel exactly where we made a | 15 asm( // We need to be able to tell the kernel exactly where we made a |
| 17 // system call. The C++ compiler likes to sometimes clone or | 16 // system call. The C++ compiler likes to sometimes clone or |
| 18 // inline code, which would inadvertently end up duplicating | 17 // inline code, which would inadvertently end up duplicating |
| (...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 166 "2:pop {r7, pc}\n" | 165 "2:pop {r7, pc}\n" |
| 167 ".cfi_endproc\n" | 166 ".cfi_endproc\n" |
| 168 #else | 167 #else |
| 169 "2:ldmfd sp!, {fp, pc}\n" | 168 "2:ldmfd sp!, {fp, pc}\n" |
| 170 #endif | 169 #endif |
| 171 ".fnend\n" | 170 ".fnend\n" |
| 172 "9:.size SyscallAsm, 9b-SyscallAsm\n" | 171 "9:.size SyscallAsm, 9b-SyscallAsm\n" |
| 173 #endif | 172 #endif |
| 174 ); // asm | 173 ); // asm |
| 175 | 174 |
| 176 #if defined(ADDRESS_SANITIZER) | 175 intptr_t SandboxSyscall(int nr, |
| 177 // ASAN will complain because we look at 6 arguments on the stack no matter | 176 intptr_t p0, intptr_t p1, intptr_t p2, |
| 178 // what. This is probably ok for a debugging feature, see crbug.com/162925. | 177 intptr_t p3, intptr_t p4, intptr_t p5) { |
| 179 __attribute__((no_address_safety_analysis)) | 178 // We rely on "intptr_t" to be the exact size as a "void *". This is |
| 180 #endif | 179 // typically true, but just in case, we add a check. The language |
| 181 intptr_t SandboxSyscall(int nr, ...) { | 180 // specification allows platforms some leeway in cases, where |
| 182 // It is most convenient for the caller to pass a variadic list of arguments. | 181 // "sizeof(void *)" is not the same as "sizeof(void (*)())". We expect |
| 183 // But this is difficult to handle in assembly code without making | 182 // that this would only be an issue for IA64, which we are currently not |
|
Jeffrey Yasskin
2012/12/03 22:40:16
Apparently intptr_t is _not_ required to be big en
Markus (顧孟勤)
2012/12/04 00:54:39
I am actually somewhat positive this would work on
Jeffrey Yasskin
2012/12/04 01:18:48
I don't know for sure what IA64 does for function
| |
| 184 // assumptions about internal implementation details of "va_list". So, we | 183 // planning on supporting. And it is even possible that this would work |
| 185 // first use C code to copy all the arguments into an array, where they are | 184 // on IA64, but for lack of actual hardware, I cannot test. |
| 186 // easily accessible to asm(). | 185 // N.B. This check will never actually print a message, as doing so requires |
| 187 // This is preferable over copying them into individual variables, which | 186 // a recursive call to SandboxSyscall(). Expect to see a stack overflow with |
| 188 // can result in too much register pressure. | 187 // a very obvious call signature. |
| 189 if (sizeof(void *)*8 != __WORDSIZE) { | 188 if (sizeof(void *) != sizeof(intptr_t)) { |
|
Jeffrey Yasskin
2012/12/03 22:27:35
You can compile-assert this. If you don't have COM
Markus (顧孟勤)
2012/12/04 00:54:39
Sweet. Yes, that's exactly what I needed here. I h
| |
| 190 SANDBOX_DIE("This can't happen! " | 189 SANDBOX_DIE("This can't happen! " |
| 191 "__WORDSIZE doesn't agree with actual size"); | 190 "\"void *\" and \"intptr_t\" have different width."); |
| 192 } | 191 } |
| 193 void *args[6]; | 192 const intptr_t args[6] = { p0, p1, p2, p3, p4, p5 }; |
|
Jeffrey Yasskin
2012/12/03 22:27:35
If the kernel could ever write on one of these arg
Markus (顧孟勤)
2012/12/04 00:54:39
If you carefully look at the assembly code, you'll
Jeffrey Yasskin
2012/12/04 01:18:48
Ok, sounds good.
| |
| 194 va_list ap; | |
| 195 | |
| 196 // System calls take a system call number (typically passed in %eax or | |
| 197 // %rax) and up to six arguments (passed in general-purpose CPU registers). | |
| 198 // | |
| 199 // On 32bit systems, all variadic arguments are passed on the stack as 32bit | |
| 200 // quantities. We can use an arbitrary 32bit type to retrieve them with | |
| 201 // va_arg() and then forward them to the kernel in the appropriate CPU | |
| 202 // register. We do not need to know whether this is an integer or a pointer | |
| 203 // value. | |
| 204 // | |
| 205 // On 64bit systems, variadic arguments can be either 32bit or 64bit wide, | |
| 206 // which would seem to make it more important that we pass the correct type | |
| 207 // to va_arg(). And we really can't know what this type is unless we have a | |
| 208 // table with function signatures for all system calls. | |
| 209 // | |
| 210 // Fortunately, on x86-64 this is less critical. The first six function | |
| 211 // arguments will be passed in CPU registers, no matter whether they were | |
| 212 // named or variadic. This only leaves us with a single argument (if present) | |
| 213 // that could be passed on the stack. And since x86-64 is little endian, | |
| 214 // it will have the correct value both for 32bit and 64bit quantities. | |
| 215 // | |
| 216 // N.B. Because of how the x86-64 ABI works, it is possible that 32bit | |
| 217 // quantities will have undefined garbage bits in the upper 32 bits of a | |
| 218 // 64bit register. This is relatively unlikely for the first five system | |
| 219 // call arguments, as the processor does automatic sign extensions and zero | |
| 220 // filling so frequently, there rarely is garbage in CPU registers. But it | |
| 221 // is quite likely for the last argument, which is passed on the stack. | |
| 222 // That's generally OK, because the kernel has the correct function | |
| 223 // signatures and knows to only inspect the LSB of a 32bit value. | |
| 224 // But callers must be careful in cases, where the compiler cannot tell | |
| 225 // the difference (e.g. when passing NULL to any system call, it must | |
| 226 // always be cast to a pointer type). | |
| 227 // The glibc implementation of syscall() has the exact same issues. | |
| 228 // In the unlikely event that this ever becomes a problem, we could add | |
| 229 // code that handles six-argument system calls specially. The number of | |
| 230 // system calls that take six arguments and expect a 32bit value in the | |
| 231 // sixth argument is very limited. | |
| 232 va_start(ap, nr); | |
| 233 args[0] = va_arg(ap, void *); | |
| 234 args[1] = va_arg(ap, void *); | |
| 235 args[2] = va_arg(ap, void *); | |
| 236 args[3] = va_arg(ap, void *); | |
| 237 args[4] = va_arg(ap, void *); | |
| 238 args[5] = va_arg(ap, void *); | |
| 239 va_end(ap); | |
| 240 | 193 |
| 241 // Invoke our file-scope assembly code. The constraints have been picked | 194 // Invoke our file-scope assembly code. The constraints have been picked |
| 242 // carefully to match what the rest of the assembly code expects in input, | 195 // carefully to match what the rest of the assembly code expects in input, |
| 243 // output, and clobbered registers. | 196 // output, and clobbered registers. |
| 244 #if defined(__i386__) | 197 #if defined(__i386__) |
| 245 intptr_t ret = nr; | 198 intptr_t ret = nr; |
| 246 asm volatile( | 199 asm volatile( |
| 247 "call SyscallAsm\n" | 200 "call SyscallAsm\n" |
| 248 // N.B. These are not the calling conventions normally used by the ABI. | 201 // N.B. These are not the calling conventions normally used by the ABI. |
| 249 : "=a"(ret) | 202 : "=a"(ret) |
| 250 : "0"(ret), "D"(args) | 203 : "0"(ret), "D"(args) |
| 251 : "esp", "memory", "ecx", "edx"); | 204 : "esp", "memory", "ecx", "edx"); |
| 252 #elif defined(__x86_64__) | 205 #elif defined(__x86_64__) |
| 253 intptr_t ret = nr; | 206 intptr_t ret = nr; |
| 254 { | 207 { |
| 255 register void **data __asm__("r12") = args; | 208 register const intptr_t *data __asm__("r12") = args; |
| 256 asm volatile( | 209 asm volatile( |
| 257 "call SyscallAsm\n" | 210 "call SyscallAsm\n" |
| 258 // N.B. These are not the calling conventions normally used by the ABI. | 211 // N.B. These are not the calling conventions normally used by the ABI. |
| 259 : "=a"(ret) | 212 : "=a"(ret) |
| 260 : "0"(ret), "r"(data) | 213 : "0"(ret), "r"(data) |
| 261 : "rsp", "memory", | 214 : "rsp", "memory", |
| 262 "rcx", "rdi", "rsi", "rdx", "r8", "r9", "r10", "r11"); | 215 "rcx", "rdi", "rsi", "rdx", "r8", "r9", "r10", "r11"); |
| 263 } | 216 } |
| 264 #elif defined(__arm__) | 217 #elif defined(__arm__) |
| 265 intptr_t ret; | 218 intptr_t ret; |
| 266 { | 219 { |
| 267 register intptr_t inout __asm__("r0") = nr; | 220 register intptr_t inout __asm__("r0") = nr; |
| 268 register void **data __asm__("r6") = args; | 221 register const intptr_t *data __asm__("r6") = args; |
| 269 asm volatile( | 222 asm volatile( |
| 270 "bl SyscallAsm\n" | 223 "bl SyscallAsm\n" |
| 271 // N.B. These are not the calling conventions normally used by the ABI. | 224 // N.B. These are not the calling conventions normally used by the ABI. |
| 272 : "=r"(inout) | 225 : "=r"(inout) |
| 273 : "0"(inout), "r"(data) | 226 : "0"(inout), "r"(data) |
| 274 : "lr", "memory", "r1", "r2", "r3", "r4", "r5" | 227 : "lr", "memory", "r1", "r2", "r3", "r4", "r5" |
| 275 #if !defined(__arm__) | 228 #if !defined(__arm__) |
| 276 // In thumb mode, we cannot use "r7" as a general purpose register, as | 229 // In thumb mode, we cannot use "r7" as a general purpose register, as |
| 277 // it is our frame pointer. We have to manually manage and preserve it. | 230 // it is our frame pointer. We have to manually manage and preserve it. |
| 278 // In ARM mode, we have a dedicated frame pointer register and "r7" is | 231 // In ARM mode, we have a dedicated frame pointer register and "r7" is |
| 279 // thus available as a general purpose register. We don't preserve it, | 232 // thus available as a general purpose register. We don't preserve it, |
| 280 // but instead mark it as clobbered. | 233 // but instead mark it as clobbered. |
| 281 , "r7" | 234 , "r7" |
| 282 #endif | 235 #endif |
| 283 ); | 236 ); |
| 284 ret = inout; | 237 ret = inout; |
| 285 } | 238 } |
| 286 #else | 239 #else |
| 287 errno = ENOSYS; | 240 errno = ENOSYS; |
| 288 intptr_t ret = -1; | 241 intptr_t ret = -1; |
| 289 #endif | 242 #endif |
| 290 return ret; | 243 return ret; |
| 291 } | 244 } |
| 292 | 245 |
| 293 } // namespace | 246 } // namespace |
| OLD | NEW |