out-of-tree kernel {module, exploit} development tool
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

preload.go 3.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. // Copyright 2020 Mikhail Klementev. All rights reserved.
  2. // Use of this source code is governed by a AGPLv3 license
  3. // (or later) that can be found in the LICENSE file.
  4. package main
  5. import (
  6. "crypto/sha1"
  7. "encoding/hex"
  8. "errors"
  9. "io/ioutil"
  10. "log"
  11. "os"
  12. "os/user"
  13. "path/filepath"
  14. "time"
  15. "github.com/go-git/go-git/v5"
  16. "code.dumpstack.io/tools/out-of-tree/config"
  17. "code.dumpstack.io/tools/out-of-tree/qemu"
  18. )
  19. var disablePreload *bool
  20. func preloadModules(q *qemu.System, ka config.Artifact, ki config.KernelInfo,
  21. dockerTimeout time.Duration) (err error) {
  22. if *disablePreload {
  23. return
  24. }
  25. for _, pm := range ka.Preload {
  26. err = preload(q, ki, pm, dockerTimeout)
  27. if err != nil {
  28. return
  29. }
  30. }
  31. return
  32. }
  33. func preload(q *qemu.System, ki config.KernelInfo, pm config.PreloadModule,
  34. dockerTimeout time.Duration) (err error) {
  35. var workPath, cache string
  36. if pm.Path != "" {
  37. log.Println("Use non-git path for preload module (no cache)")
  38. workPath = pm.Path
  39. } else if pm.Repo != "" {
  40. workPath, cache, err = cloneOrPull(pm.Repo, ki)
  41. if err != nil {
  42. return
  43. }
  44. } else {
  45. errors.New("No repo/path in preload entry")
  46. }
  47. err = buildAndInsmod(workPath, q, ki, dockerTimeout, cache)
  48. if err != nil {
  49. return
  50. }
  51. time.Sleep(pm.TimeoutAfterLoad.Duration)
  52. return
  53. }
  54. func buildAndInsmod(workPath string, q *qemu.System, ki config.KernelInfo,
  55. dockerTimeout time.Duration, cache string) (err error) {
  56. tmp, err := ioutil.TempDir("", "out-of-tree_")
  57. if err != nil {
  58. return
  59. }
  60. defer os.RemoveAll(tmp)
  61. var artifact string
  62. if exists(cache) {
  63. artifact = cache
  64. } else {
  65. artifact, err = buildPreload(workPath, tmp, ki, dockerTimeout)
  66. if err != nil {
  67. return
  68. }
  69. if cache != "" {
  70. err = copyFile(artifact, cache)
  71. if err != nil {
  72. return
  73. }
  74. }
  75. }
  76. output, err := q.CopyAndInsmod(artifact)
  77. if err != nil {
  78. log.Println(output)
  79. return
  80. }
  81. return
  82. }
  83. func buildPreload(workPath, tmp string, ki config.KernelInfo,
  84. dockerTimeout time.Duration) (artifact string, err error) {
  85. ka, err := config.ReadArtifactConfig(workPath + "/.out-of-tree.toml")
  86. if err != nil {
  87. return
  88. }
  89. ka.SourcePath = workPath
  90. km := config.KernelMask{DistroType: ki.DistroType,
  91. DistroRelease: ki.DistroRelease,
  92. ReleaseMask: ki.KernelRelease,
  93. }
  94. ka.SupportedKernels = []config.KernelMask{km}
  95. if ka.Docker.Timeout.Duration != 0 {
  96. dockerTimeout = ka.Docker.Timeout.Duration
  97. }
  98. artifact, _, err = build(tmp, ka, ki, dockerTimeout)
  99. return
  100. }
  101. func cloneOrPull(repo string, ki config.KernelInfo) (workPath, cache string, err error) {
  102. usr, err := user.Current()
  103. if err != nil {
  104. return
  105. }
  106. base := filepath.Join(usr.HomeDir, "/.out-of-tree/preload/")
  107. workPath = filepath.Join(base, "/repos/", sha1sum(repo))
  108. var r *git.Repository
  109. if exists(workPath) {
  110. r, err = git.PlainOpen(workPath)
  111. if err != nil {
  112. return
  113. }
  114. var w *git.Worktree
  115. w, err = r.Worktree()
  116. if err != nil {
  117. return
  118. }
  119. err = w.Pull(&git.PullOptions{})
  120. if err != nil && err != git.NoErrAlreadyUpToDate {
  121. log.Println(repo, "pull error:", err)
  122. }
  123. } else {
  124. r, err = git.PlainClone(workPath, false, &git.CloneOptions{URL: repo})
  125. if err != nil {
  126. return
  127. }
  128. }
  129. ref, err := r.Head()
  130. if err != nil {
  131. return
  132. }
  133. cachedir := filepath.Join(base, "/cache/")
  134. os.MkdirAll(cachedir, 0700)
  135. filename := sha1sum(repo + ki.KernelPath + ref.Hash().String())
  136. cache = filepath.Join(cachedir, filename)
  137. return
  138. }
  139. func sha1sum(data string) string {
  140. h := sha1.Sum([]byte(data))
  141. return hex.EncodeToString(h[:])
  142. }