...

Source file src/github.com/chaos-mesh/chaos-mesh/pkg/time/fake_image_linux.go

Documentation: github.com/chaos-mesh/chaos-mesh/pkg/time

     1  // Copyright 2021 Chaos Mesh 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  
    16  package time
    17  
    18  import (
    19  	"bytes"
    20  	"runtime"
    21  
    22  	"github.com/go-logr/logr"
    23  	"github.com/pkg/errors"
    24  
    25  	"github.com/chaos-mesh/chaos-mesh/pkg/cerr"
    26  	"github.com/chaos-mesh/chaos-mesh/pkg/mapreader"
    27  	"github.com/chaos-mesh/chaos-mesh/pkg/ptrace"
    28  )
    29  
    30  // vdsoEntryName is the name of the vDSO entry
    31  const vdsoEntryName = "[vdso]"
    32  
    33  // FakeImage introduce the replacement of VDSO ELF entry and customizable variables.
    34  // FakeImage could be constructed by LoadFakeImageFromEmbedFs(), and then used by FakeClockInjector.
    35  type FakeImage struct {
    36  	// symbolName is the name of the symbol to be replaced.
    37  	symbolName string
    38  	// content presents .text section which has been "manually relocation", the address of extern variables have been calculated manually
    39  	content []byte
    40  	// offset stores the table with variable name, and it's address in content.
    41  	// the key presents extern variable name, ths value is the address/offset within the content.
    42  	offset map[string]int
    43  	// OriginFuncCode stores the raw func code like getTimeOfDay & ClockGetTime.
    44  	OriginFuncCode []byte
    45  	// OriginAddress stores the origin address of OriginFuncCode.
    46  	OriginAddress uint64
    47  	// fakeEntry stores the fake entry
    48  	fakeEntry *mapreader.Entry
    49  
    50  	logger logr.Logger
    51  }
    52  
    53  func NewFakeImage(symbolName string, content []byte, offset map[string]int, logger logr.Logger) *FakeImage {
    54  	return &FakeImage{symbolName: symbolName, content: content, offset: offset, logger: logger}
    55  }
    56  
    57  // AttachToProcess would use ptrace to replace the VDSO ELF entry with FakeImage.
    58  // Each item in parameter "variables" needs a corresponding entry in FakeImage.offset.
    59  func (it *FakeImage) AttachToProcess(pid int, variables map[string]uint64) (err error) {
    60  	if len(variables) != len(it.offset) {
    61  		return errors.New("fake image: extern variable number not match")
    62  	}
    63  
    64  	runtime.LockOSThread()
    65  	defer func() {
    66  		runtime.UnlockOSThread()
    67  	}()
    68  
    69  	program, err := ptrace.Trace(pid, it.logger.WithName("ptrace").WithValues("pid", pid))
    70  	if err != nil {
    71  		return errors.Wrapf(err, "ptrace on target process, pid: %d", pid)
    72  	}
    73  	defer func() {
    74  		err = program.Detach()
    75  		if err != nil {
    76  			it.logger.Error(err, "fail to detach program", "pid", program.Pid())
    77  		}
    78  	}()
    79  
    80  	vdsoEntry, err := FindVDSOEntry(program)
    81  	if err != nil {
    82  		return errors.Wrapf(err, "PID : %d", pid)
    83  	}
    84  
    85  	fakeEntry, err := it.FindInjectedImage(program, len(variables))
    86  	if err != nil {
    87  		return errors.Wrapf(err, "PID : %d", pid)
    88  	}
    89  	// target process has not been injected yet
    90  	if fakeEntry == nil {
    91  		fakeEntry, err = it.InjectFakeImage(program, vdsoEntry)
    92  		if err != nil {
    93  			return errors.Wrapf(err, "injecting fake image , PID : %d", pid)
    94  		}
    95  		defer func() {
    96  			if err != nil {
    97  				errIn := it.TryReWriteFakeImage(program)
    98  				if errIn != nil {
    99  					it.logger.Error(errIn, "rewrite fail, recover fail")
   100  				}
   101  				it.OriginFuncCode = nil
   102  				it.OriginAddress = 0
   103  			}
   104  		}()
   105  	}
   106  
   107  	for k, v := range variables {
   108  		err = it.SetVarUint64(program, fakeEntry, k, v)
   109  
   110  		if err != nil {
   111  			return errors.Wrapf(err, "set %s for time skew, pid: %d", k, pid)
   112  		}
   113  	}
   114  
   115  	return
   116  }
   117  
   118  func FindVDSOEntry(program *ptrace.TracedProgram) (*mapreader.Entry, error) {
   119  	var vdsoEntry *mapreader.Entry
   120  	for index := range program.Entries {
   121  		// reverse loop is faster
   122  		e := program.Entries[len(program.Entries)-index-1]
   123  		if e.Path == vdsoEntryName {
   124  			vdsoEntry = &e
   125  			break
   126  		}
   127  	}
   128  	if vdsoEntry == nil {
   129  		return nil, cerr.NotFound("VDSOEntry").Err()
   130  	}
   131  	return vdsoEntry, nil
   132  }
   133  
   134  // FindInjectedImage find injected image to avoid redundant inject.
   135  func (it *FakeImage) FindInjectedImage(program *ptrace.TracedProgram, varNum int) (*mapreader.Entry, error) {
   136  	it.logger.Info("finding injected image")
   137  
   138  	// minus tailing variable part
   139  	// every variable has 8 bytes
   140  	if it.fakeEntry != nil {
   141  		content, err := program.ReadSlice(it.fakeEntry.StartAddress, it.fakeEntry.EndAddress-it.fakeEntry.StartAddress)
   142  		if err != nil {
   143  			it.logger.Info("ReadSlice fail")
   144  			return nil, nil
   145  		}
   146  		if varNum*8 > len(it.content) {
   147  			return nil, errors.New("variable num bigger than content num")
   148  		}
   149  		contentWithoutVariable := (*content)[:len(it.content)-varNum*varLength]
   150  		expectedContentWithoutVariable := it.content[:len(it.content)-varNum*varLength]
   151  		it.logger.Info("successfully read slice", "content", contentWithoutVariable, "expected content", expectedContentWithoutVariable)
   152  
   153  		if bytes.Equal(contentWithoutVariable, expectedContentWithoutVariable) {
   154  			it.logger.Info("slice found")
   155  			return it.fakeEntry, nil
   156  		}
   157  		it.logger.Info("slice not found")
   158  	}
   159  	return nil, nil
   160  }
   161  
   162  // InjectFakeImage Usage CheckList:
   163  // When error : TryReWriteFakeImage after InjectFakeImage.
   164  func (it *FakeImage) InjectFakeImage(program *ptrace.TracedProgram,
   165  	vdsoEntry *mapreader.Entry) (*mapreader.Entry, error) {
   166  	fakeEntry, err := program.MmapSlice(it.content)
   167  	if err != nil {
   168  		return nil, errors.Wrapf(err, "mmap fake image")
   169  	}
   170  	it.fakeEntry = fakeEntry
   171  	originAddr, size, err := program.FindSymbolInEntry(it.symbolName, vdsoEntry)
   172  	if err != nil {
   173  		return nil, errors.Wrapf(err, "find origin %s in vdso", it.symbolName)
   174  	}
   175  	funcBytes, err := program.ReadSlice(originAddr, size)
   176  	if err != nil {
   177  		return nil, errors.Wrapf(err, "ReadSlice failed")
   178  	}
   179  	err = program.JumpToFakeFunc(originAddr, fakeEntry.StartAddress)
   180  	if err != nil {
   181  		errIn := it.TryReWriteFakeImage(program)
   182  		if errIn != nil {
   183  			it.logger.Error(errIn, "rewrite fail, recover fail")
   184  		}
   185  		return nil, errors.Wrapf(err, "override origin %s", it.symbolName)
   186  	}
   187  
   188  	it.OriginFuncCode = *funcBytes
   189  	it.OriginAddress = originAddr
   190  	return fakeEntry, nil
   191  }
   192  
   193  func (it *FakeImage) TryReWriteFakeImage(program *ptrace.TracedProgram) error {
   194  	if it.OriginFuncCode != nil {
   195  		err := program.PtraceWriteSlice(it.OriginAddress, it.OriginFuncCode)
   196  		if err != nil {
   197  			return err
   198  		}
   199  		it.OriginFuncCode = nil
   200  		it.OriginAddress = 0
   201  	}
   202  	return nil
   203  }
   204  
   205  // Recover the injected image. If injected image not found ,
   206  // Recover will not return error.
   207  func (it *FakeImage) Recover(pid int, vars map[string]uint64) error {
   208  	runtime.LockOSThread()
   209  	defer func() {
   210  		runtime.UnlockOSThread()
   211  	}()
   212  	if it.OriginFuncCode == nil {
   213  		return nil
   214  	}
   215  	program, err := ptrace.Trace(pid, it.logger.WithName("ptrace").WithValues("pid", pid))
   216  	if err != nil {
   217  		return errors.Wrapf(err, "ptrace on target process, pid: %d", pid)
   218  	}
   219  	defer func() {
   220  		err = program.Detach()
   221  		if err != nil {
   222  			it.logger.Error(err, "fail to detach program", "pid", program.Pid())
   223  		}
   224  	}()
   225  
   226  	fakeEntry, err := it.FindInjectedImage(program, len(vars))
   227  	if err != nil {
   228  		return errors.Wrapf(err, "FindInjectedImage , pid: %d", pid)
   229  	}
   230  	if fakeEntry == nil {
   231  		return nil
   232  	}
   233  
   234  	err = it.TryReWriteFakeImage(program)
   235  	return err
   236  }
   237