ptrace_linux.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. // Copyright The OpenTelemetry 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. package ptrace
  15. import (
  16. "fmt"
  17. "k8s.io/klog/v2"
  18. "os"
  19. "strconv"
  20. "strings"
  21. "syscall"
  22. "golang.org/x/sys/unix"
  23. "github.com/go-logr/logr"
  24. "github.com/pkg/errors"
  25. )
  26. const waitPidErrorMessage = "waitpid ret value: %d"
  27. const (
  28. // MADV_POPULATE_READ.
  29. MadvisePopulateRead = 0x16
  30. // MADV_POPULATE_WRITE.
  31. MadvisePopulateWrite = 0x17
  32. )
  33. var threadRetryLimit = 10
  34. // TracedProgram is a program traced by ptrace.
  35. type TracedProgram struct {
  36. pid int
  37. tids []int
  38. backupRegs *syscall.PtraceRegs
  39. backupCode []byte
  40. logger logr.Logger
  41. }
  42. // Pid return the pid of traced program.
  43. func (p *TracedProgram) Pid() int {
  44. return p.pid
  45. }
  46. func waitPid(pid int) error {
  47. ret, err := unix.Wait4(pid, nil, unix.WALL, nil)
  48. if err != nil {
  49. return err
  50. }
  51. if ret == pid {
  52. return nil
  53. }
  54. return errors.Errorf(waitPidErrorMessage, ret)
  55. }
  56. // NewTracedProgram ptrace all threads of a process.
  57. func NewTracedProgram(pid int) (*TracedProgram, error) {
  58. tidMap := make(map[int]bool)
  59. retryCount := make(map[int]int)
  60. // iterate over the thread group, until it doens't change
  61. //
  62. // we have tried several ways to ensure that we have stopped all the tasks:
  63. // 1. iterating over and over again to make sure all of them are tracee
  64. // 2. send `SIGSTOP` signal
  65. // ...
  66. // only the first way finally worked for every situations
  67. for {
  68. threads, err := os.ReadDir(fmt.Sprintf("/proc/%d/task", pid))
  69. if err != nil {
  70. return nil, errors.WithStack(err)
  71. }
  72. // judge whether `threads` is a subset of `tidMap`
  73. subset := true
  74. tids := make(map[int]bool)
  75. for _, thread := range threads {
  76. tid64, err := strconv.ParseInt(thread.Name(), 10, 32)
  77. if err != nil {
  78. return nil, errors.WithStack(err)
  79. }
  80. tid := int(tid64)
  81. _, ok := tidMap[tid]
  82. if ok {
  83. tids[tid] = true
  84. continue
  85. }
  86. subset = false
  87. err = syscall.PtraceAttach(tid)
  88. if err != nil {
  89. _, ok := retryCount[tid]
  90. if !ok {
  91. retryCount[tid] = 1
  92. } else {
  93. retryCount[tid]++
  94. }
  95. if retryCount[tid] < threadRetryLimit {
  96. klog.Info("retry attaching thread", "tid", tid, "retryCount", retryCount[tid], "limit", threadRetryLimit)
  97. continue
  98. }
  99. if !strings.Contains(err.Error(), "no such process") {
  100. return nil, errors.WithStack(err)
  101. }
  102. continue
  103. }
  104. err = waitPid(tid)
  105. if err != nil {
  106. e := syscall.PtraceDetach(tid)
  107. if e != nil && !strings.Contains(e.Error(), "no such process") {
  108. klog.Error(e, "detach failed", "tid", tid)
  109. }
  110. return nil, errors.WithStack(err)
  111. }
  112. klog.Info("attach successfully", "tid", tid)
  113. tids[tid] = true
  114. tidMap[tid] = true
  115. }
  116. if subset {
  117. tidMap = tids
  118. break
  119. }
  120. }
  121. var tids []int
  122. for key := range tidMap {
  123. tids = append(tids, key)
  124. }
  125. program := &TracedProgram{
  126. pid: pid,
  127. tids: tids,
  128. backupRegs: &syscall.PtraceRegs{},
  129. backupCode: make([]byte, syscallInstrSize),
  130. }
  131. return program, nil
  132. }
  133. // Detach detaches from all threads of the processes.
  134. func (p *TracedProgram) Detach() error {
  135. for _, tid := range p.tids {
  136. err := syscall.PtraceDetach(tid)
  137. if err != nil {
  138. if !strings.Contains(err.Error(), "no such process") {
  139. return errors.WithStack(err)
  140. }
  141. }
  142. }
  143. return nil
  144. }
  145. // Protect will backup regs and rip into fields.
  146. func (p *TracedProgram) Protect() error {
  147. err := getRegs(p.pid, p.backupRegs)
  148. if err != nil {
  149. return errors.WithStack(err)
  150. }
  151. _, err = syscall.PtracePeekData(p.pid, getIP(p.backupRegs), p.backupCode)
  152. if err != nil {
  153. return errors.WithStack(err)
  154. }
  155. return nil
  156. }
  157. // Restore will restore regs and rip from fields.
  158. func (p *TracedProgram) Restore() error {
  159. err := setRegs(p.pid, p.backupRegs)
  160. if err != nil {
  161. return errors.WithStack(err)
  162. }
  163. _, err = syscall.PtracePokeData(p.pid, getIP(p.backupRegs), p.backupCode)
  164. if err != nil {
  165. return errors.WithStack(err)
  166. }
  167. return nil
  168. }
  169. // Wait waits until the process stops.
  170. func (p *TracedProgram) Wait() error {
  171. _, err := syscall.Wait4(p.pid, nil, 0, nil)
  172. return err
  173. }
  174. // Step moves one step forward.
  175. func (p *TracedProgram) Step() error {
  176. err := syscall.PtraceSingleStep(p.pid)
  177. if err != nil {
  178. return errors.WithStack(err)
  179. }
  180. return p.Wait()
  181. }
  182. // SetMemLockInfinity sets the memlock rlimit to infinity.
  183. func (p *TracedProgram) SetMemLockInfinity() error {
  184. // Requires CAP_SYS_RESOURCE.
  185. newLimit := unix.Rlimit{Cur: unix.RLIM_INFINITY, Max: unix.RLIM_INFINITY}
  186. if err := unix.Prlimit(p.pid, unix.RLIMIT_MEMLOCK, &newLimit, nil); err != nil {
  187. return fmt.Errorf("failed to set memlock rlimit: %w", err)
  188. }
  189. return nil
  190. }
  191. // Mmap runs mmap syscall.
  192. func (p *TracedProgram) Mmap(length uint64, fd uint64) (uint64, error) {
  193. return p.Syscall(syscall.SYS_MMAP, 0, length, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_ANON|syscall.MAP_PRIVATE|syscall.MAP_POPULATE|syscall.MAP_LOCKED, fd, 0)
  194. }
  195. // Madvise runs madvise syscall.
  196. func (p *TracedProgram) Madvise(addr uint64, length uint64) error {
  197. //advice := uint64(syscall.MADV_WILLNEED)
  198. //ver, err := utils.GetLinuxKernelVersion()
  199. //if err != nil {
  200. // return errors.WithStack(err)
  201. //}
  202. //
  203. //minVersion := version.Must(version.NewVersion("5.14"))
  204. //p.logger.Info("Detected linux kernel version", "version", ver)
  205. //if ver.GreaterThanOrEqual(minVersion) {
  206. // advice = syscall.MADV_WILLNEED | MadvisePopulateRead | MadvisePopulateWrite
  207. //}
  208. //
  209. //_, err = p.Syscall(syscall.SYS_MADVISE, addr, length, advice, 0, 0, 0)
  210. //return err
  211. return nil
  212. }
  213. // Mlock runs mlock syscall.
  214. func (p *TracedProgram) Mlock(addr uint64, length uint64) error {
  215. ret, err := p.Syscall(syscall.SYS_MLOCK, addr, length, 0, 0, 0, 0)
  216. p.logger.Info("mlock ret", "ret", ret)
  217. return err
  218. }