github.com/MerlinKodo/gvisor@v0.0.0-20231110090155-957f62ecf90e/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  	"github.com/MerlinKodo/gvisor/pkg/context"
    25  	"github.com/MerlinKodo/gvisor/pkg/marshal/primitive"
    26  	"github.com/MerlinKodo/gvisor/pkg/sentry/arch"
    27  	"github.com/MerlinKodo/gvisor/pkg/sentry/platform/systrap/sysmsg"
    28  	"github.com/MerlinKodo/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  }