gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/sentry/platform/systrap/usertrap/usertrap_amd64_unsafe.go (about) 1 // Copyright 2020 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //go:build amd64 16 // +build amd64 17 18 package usertrap 19 20 import ( 21 "encoding/binary" 22 "unsafe" 23 24 "gvisor.dev/gvisor/pkg/context" 25 "gvisor.dev/gvisor/pkg/marshal/primitive" 26 "gvisor.dev/gvisor/pkg/sentry/arch" 27 "gvisor.dev/gvisor/pkg/sentry/platform/systrap/sysmsg" 28 "gvisor.dev/gvisor/pkg/usermem" 29 ) 30 31 // addTrapLocked constructs a trampoline for a specified syscall. 32 // 33 // mm.UserTrap.Lock has to be taken. 34 func (s *State) addTrapLocked(ctx context.Context, ac *arch.Context64, mm memoryManager, sysno uint32) (uint64, error) { 35 trapAddr, err := s.newTrapLocked(ctx, mm) 36 if err != nil { 37 return 0, err 38 } 39 40 // First eight bytes is an address which points to the 9th byte, they 41 // are used as an argument for the jmp instruction. 42 // 43 // Then here is the code of the syscall trampoline. 44 // First, we need to lock the sysmsg struct by setting StatePrep. This 45 // is used to synchronise with sighandler which uses the same struct 46 // sysmsg. And we need to guarantee that the current thread will not be 47 // interrupted in syshandler, because the sysmsg struct isn't saved on 48 // S/R. 49 // A thread stack can't be change, so the call instruction can't be 50 // used and we need to save values of stack and instruction registers, 51 // switch to the syshandler stack and call the jmp instruction to 52 // syshandler: 53 // mov sysmsg.ThreadStatePrep, %gs:offset(msg.State) 54 // mov %rsp,%gs:0x20 // msg.AppStack 55 // mov %gs:0x18,%rsp // msg.SyshandlerStack 56 // movabs $ret_addr, %rax 57 // mov %rax,%gs:0x8 // msg.RetAddr 58 // mov sysno,%eax 59 // jmpq *%gs:0x10 // msg.Syshandler 60 trap := []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 61 // msg.State = sysmsg.ThreadStatePrep 62 /*08*/ 0x65, 0xc7, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov $X, %gs:OFFSET 63 /*20*/ 0x65, 0x48, 0x89, 0x24, 0x25, 0x20, 0x00, 0x00, 0x00, // mov %rsp,%gs:0x20 64 /*29*/ 0x65, 0x48, 0x8b, 0x24, 0x25, 0x18, 0x00, 0x00, 0x00, // mov %gs:0x18,%rsp 65 /*38*/ 0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // movabs $ret_addr, %rax 66 /*48*/ 0x65, 0x48, 0x89, 0x04, 0x25, 0x08, 0x00, 0x00, 0x00, // mov %rax,%gs:0x8 67 /*57*/ 0xb8, 0x00, 0x00, 0x00, 0x00, // mov sysno,%eax 68 /*62*/ 0x65, 0xff, 0x24, 0x25, 0x10, 0x00, 0x00, 0x00, // jmpq *%gs:0x10 69 } 70 binary.LittleEndian.PutUint64(trap[40:48], uint64(ac.IP())) 71 binary.LittleEndian.PutUint32(trap[58:62], sysno) 72 binary.LittleEndian.PutUint64(trap[:8], uint64(trapAddr)+8) 73 74 var msg *sysmsg.Msg 75 binary.LittleEndian.PutUint32(trap[12:16], uint32(unsafe.Offsetof(msg.State))) 76 binary.LittleEndian.PutUint32(trap[16:20], uint32(sysmsg.ThreadStatePrep)) 77 binary.LittleEndian.PutUint32(trap[25:29], uint32(unsafe.Offsetof(msg.AppStack))) 78 binary.LittleEndian.PutUint32(trap[34:38], uint32(unsafe.Offsetof(msg.SyshandlerStack))) 79 binary.LittleEndian.PutUint32(trap[53:57], uint32(unsafe.Offsetof(msg.RetAddr))) 80 binary.LittleEndian.PutUint32(trap[66:70], uint32(unsafe.Offsetof(msg.Syshandler))) 81 82 iocc := usermem.IOCopyContext{ 83 Ctx: ctx, 84 IO: mm, 85 Opts: usermem.IOOpts{ 86 IgnorePermissions: true, 87 }, 88 } 89 _, err = primitive.CopyByteSliceOut(&iocc, trapAddr, trap[:]) 90 return uint64(trapAddr), err 91 }