kprobe.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. package tracefs
  2. import (
  3. "crypto/rand"
  4. "errors"
  5. "fmt"
  6. "os"
  7. "path/filepath"
  8. "runtime"
  9. "strings"
  10. "syscall"
  11. "github.com/cilium/ebpf/internal"
  12. "github.com/cilium/ebpf/internal/unix"
  13. )
  14. var (
  15. ErrInvalidInput = errors.New("invalid input")
  16. ErrInvalidMaxActive = errors.New("can only set maxactive on kretprobes")
  17. )
  18. //go:generate stringer -type=ProbeType -linecomment
  19. type ProbeType uint8
  20. const (
  21. Kprobe ProbeType = iota // kprobe
  22. Uprobe // uprobe
  23. )
  24. func (pt ProbeType) eventsFile() (*os.File, error) {
  25. path, err := sanitizeTracefsPath(fmt.Sprintf("%s_events", pt.String()))
  26. if err != nil {
  27. return nil, err
  28. }
  29. return os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0666)
  30. }
  31. type ProbeArgs struct {
  32. Type ProbeType
  33. Symbol, Group, Path string
  34. Offset, RefCtrOffset, Cookie uint64
  35. Pid, RetprobeMaxActive int
  36. Ret bool
  37. }
  38. // RandomGroup generates a pseudorandom string for use as a tracefs group name.
  39. // Returns an error when the output string would exceed 63 characters (kernel
  40. // limitation), when rand.Read() fails or when prefix contains characters not
  41. // allowed by IsValidTraceID.
  42. func RandomGroup(prefix string) (string, error) {
  43. if !validIdentifier(prefix) {
  44. return "", fmt.Errorf("prefix '%s' must be alphanumeric or underscore: %w", prefix, ErrInvalidInput)
  45. }
  46. b := make([]byte, 8)
  47. if _, err := rand.Read(b); err != nil {
  48. return "", fmt.Errorf("reading random bytes: %w", err)
  49. }
  50. group := fmt.Sprintf("%s_%x", prefix, b)
  51. if len(group) > 63 {
  52. return "", fmt.Errorf("group name '%s' cannot be longer than 63 characters: %w", group, ErrInvalidInput)
  53. }
  54. return group, nil
  55. }
  56. // validIdentifier implements the equivalent of a regex match
  57. // against "^[a-zA-Z_][0-9a-zA-Z_]*$".
  58. //
  59. // Trace event groups, names and kernel symbols must adhere to this set
  60. // of characters. Non-empty, first character must not be a number, all
  61. // characters must be alphanumeric or underscore.
  62. func validIdentifier(s string) bool {
  63. if len(s) < 1 {
  64. return false
  65. }
  66. for i, c := range []byte(s) {
  67. switch {
  68. case c >= 'a' && c <= 'z':
  69. case c >= 'A' && c <= 'Z':
  70. case c == '_':
  71. case i > 0 && c >= '0' && c <= '9':
  72. default:
  73. return false
  74. }
  75. }
  76. return true
  77. }
  78. func sanitizeTracefsPath(path ...string) (string, error) {
  79. base, err := getTracefsPath()
  80. if err != nil {
  81. return "", err
  82. }
  83. l := filepath.Join(path...)
  84. p := filepath.Join(base, l)
  85. if !strings.HasPrefix(p, base) {
  86. return "", fmt.Errorf("path '%s' attempts to escape base path '%s': %w", l, base, ErrInvalidInput)
  87. }
  88. return p, nil
  89. }
  90. // getTracefsPath will return a correct path to the tracefs mount point.
  91. // Since kernel 4.1 tracefs should be mounted by default at /sys/kernel/tracing,
  92. // but may be also be available at /sys/kernel/debug/tracing if debugfs is mounted.
  93. // The available tracefs paths will depends on distribution choices.
  94. var getTracefsPath = internal.Memoize(func() (string, error) {
  95. for _, p := range []struct {
  96. path string
  97. fsType int64
  98. }{
  99. {"/sys/kernel/tracing", unix.TRACEFS_MAGIC},
  100. {"/sys/kernel/debug/tracing", unix.TRACEFS_MAGIC},
  101. // RHEL/CentOS
  102. {"/sys/kernel/debug/tracing", unix.DEBUGFS_MAGIC},
  103. } {
  104. if fsType, err := internal.FSType(p.path); err == nil && fsType == p.fsType {
  105. return p.path, nil
  106. }
  107. }
  108. return "", errors.New("neither debugfs nor tracefs are mounted")
  109. })
  110. // sanitizeIdentifier replaces every invalid character for the tracefs api with an underscore.
  111. //
  112. // It is equivalent to calling regexp.MustCompile("[^a-zA-Z0-9]+").ReplaceAllString("_").
  113. func sanitizeIdentifier(s string) string {
  114. var skip bool
  115. return strings.Map(func(c rune) rune {
  116. switch {
  117. case c >= 'a' && c <= 'z',
  118. c >= 'A' && c <= 'Z',
  119. c >= '0' && c <= '9':
  120. skip = false
  121. return c
  122. case skip:
  123. return -1
  124. default:
  125. skip = true
  126. return '_'
  127. }
  128. }, s)
  129. }
  130. // EventID reads a trace event's ID from tracefs given its group and name.
  131. // The kernel requires group and name to be alphanumeric or underscore.
  132. func EventID(group, name string) (uint64, error) {
  133. if !validIdentifier(group) {
  134. return 0, fmt.Errorf("invalid tracefs group: %q", group)
  135. }
  136. if !validIdentifier(name) {
  137. return 0, fmt.Errorf("invalid tracefs name: %q", name)
  138. }
  139. path, err := sanitizeTracefsPath("events", group, name, "id")
  140. if err != nil {
  141. return 0, err
  142. }
  143. tid, err := internal.ReadUint64FromFile("%d\n", path)
  144. if errors.Is(err, os.ErrNotExist) {
  145. return 0, err
  146. }
  147. if err != nil {
  148. return 0, fmt.Errorf("reading trace event ID of %s/%s: %w", group, name, err)
  149. }
  150. return tid, nil
  151. }
  152. func probePrefix(ret bool, maxActive int) string {
  153. if ret {
  154. if maxActive > 0 {
  155. return fmt.Sprintf("r%d", maxActive)
  156. }
  157. return "r"
  158. }
  159. return "p"
  160. }
  161. // Event represents an entry in a tracefs probe events file.
  162. type Event struct {
  163. typ ProbeType
  164. group, name string
  165. // event id allocated by the kernel. 0 if the event has already been removed.
  166. id uint64
  167. }
  168. // NewEvent creates a new ephemeral trace event.
  169. //
  170. // Returns os.ErrNotExist if symbol is not a valid
  171. // kernel symbol, or if it is not traceable with kprobes. Returns os.ErrExist
  172. // if a probe with the same group and symbol already exists. Returns an error if
  173. // args.RetprobeMaxActive is used on non kprobe types. Returns ErrNotSupported if
  174. // the kernel is too old to support kretprobe maxactive.
  175. func NewEvent(args ProbeArgs) (*Event, error) {
  176. // Before attempting to create a trace event through tracefs,
  177. // check if an event with the same group and name already exists.
  178. // Kernels 4.x and earlier don't return os.ErrExist on writing a duplicate
  179. // entry, so we need to rely on reads for detecting uniqueness.
  180. eventName := sanitizeIdentifier(args.Symbol)
  181. _, err := EventID(args.Group, eventName)
  182. if err == nil {
  183. return nil, fmt.Errorf("trace event %s/%s: %w", args.Group, eventName, os.ErrExist)
  184. }
  185. if err != nil && !errors.Is(err, os.ErrNotExist) {
  186. return nil, fmt.Errorf("checking trace event %s/%s: %w", args.Group, eventName, err)
  187. }
  188. // Open the kprobe_events file in tracefs.
  189. f, err := args.Type.eventsFile()
  190. if err != nil {
  191. return nil, err
  192. }
  193. defer f.Close()
  194. var pe, token string
  195. switch args.Type {
  196. case Kprobe:
  197. // The kprobe_events syntax is as follows (see Documentation/trace/kprobetrace.txt):
  198. // p[:[GRP/]EVENT] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS] : Set a probe
  199. // r[MAXACTIVE][:[GRP/]EVENT] [MOD:]SYM[+0] [FETCHARGS] : Set a return probe
  200. // -:[GRP/]EVENT : Clear a probe
  201. //
  202. // Some examples:
  203. // r:ebpf_1234/r_my_kretprobe nf_conntrack_destroy
  204. // p:ebpf_5678/p_my_kprobe __x64_sys_execve
  205. //
  206. // Leaving the kretprobe's MAXACTIVE set to 0 (or absent) will make the
  207. // kernel default to NR_CPUS. This is desired in most eBPF cases since
  208. // subsampling or rate limiting logic can be more accurately implemented in
  209. // the eBPF program itself.
  210. // See Documentation/kprobes.txt for more details.
  211. if args.RetprobeMaxActive != 0 && !args.Ret {
  212. return nil, ErrInvalidMaxActive
  213. }
  214. token = KprobeToken(args)
  215. pe = fmt.Sprintf("%s:%s/%s %s", probePrefix(args.Ret, args.RetprobeMaxActive), args.Group, eventName, token)
  216. case Uprobe:
  217. // The uprobe_events syntax is as follows:
  218. // p[:[GRP/]EVENT] PATH:OFFSET [FETCHARGS] : Set a probe
  219. // r[:[GRP/]EVENT] PATH:OFFSET [FETCHARGS] : Set a return probe
  220. // -:[GRP/]EVENT : Clear a probe
  221. //
  222. // Some examples:
  223. // r:ebpf_1234/readline /bin/bash:0x12345
  224. // p:ebpf_5678/main_mySymbol /bin/mybin:0x12345(0x123)
  225. //
  226. // See Documentation/trace/uprobetracer.txt for more details.
  227. if args.RetprobeMaxActive != 0 {
  228. return nil, ErrInvalidMaxActive
  229. }
  230. token = UprobeToken(args)
  231. pe = fmt.Sprintf("%s:%s/%s %s", probePrefix(args.Ret, 0), args.Group, eventName, token)
  232. }
  233. _, err = f.WriteString(pe)
  234. // Since commit 97c753e62e6c, ENOENT is correctly returned instead of EINVAL
  235. // when trying to create a retprobe for a missing symbol.
  236. if errors.Is(err, os.ErrNotExist) {
  237. return nil, fmt.Errorf("token %s: not found: %w", token, err)
  238. }
  239. // Since commit ab105a4fb894, EILSEQ is returned when a kprobe sym+offset is resolved
  240. // to an invalid insn boundary. The exact conditions that trigger this error are
  241. // arch specific however.
  242. if errors.Is(err, syscall.EILSEQ) {
  243. return nil, fmt.Errorf("token %s: bad insn boundary: %w", token, os.ErrNotExist)
  244. }
  245. // ERANGE is returned when the `SYM[+offs]` token is too big and cannot
  246. // be resolved.
  247. if errors.Is(err, syscall.ERANGE) {
  248. return nil, fmt.Errorf("token %s: offset too big: %w", token, os.ErrNotExist)
  249. }
  250. if err != nil {
  251. return nil, fmt.Errorf("token %s: writing '%s': %w", token, pe, err)
  252. }
  253. // Get the newly-created trace event's id.
  254. tid, err := EventID(args.Group, eventName)
  255. if args.RetprobeMaxActive != 0 && errors.Is(err, os.ErrNotExist) {
  256. // Kernels < 4.12 don't support maxactive and therefore auto generate
  257. // group and event names from the symbol and offset. The symbol is used
  258. // without any sanitization.
  259. // See https://elixir.bootlin.com/linux/v4.10/source/kernel/trace/trace_kprobe.c#L712
  260. event := fmt.Sprintf("kprobes/r_%s_%d", args.Symbol, args.Offset)
  261. if err := removeEvent(args.Type, event); err != nil {
  262. return nil, fmt.Errorf("failed to remove spurious maxactive event: %s", err)
  263. }
  264. return nil, fmt.Errorf("create trace event with non-default maxactive: %w", internal.ErrNotSupported)
  265. }
  266. if err != nil {
  267. return nil, fmt.Errorf("get trace event id: %w", err)
  268. }
  269. evt := &Event{args.Type, args.Group, eventName, tid}
  270. runtime.SetFinalizer(evt, (*Event).Close)
  271. return evt, nil
  272. }
  273. // Close removes the event from tracefs.
  274. //
  275. // Returns os.ErrClosed if the event has already been closed before.
  276. func (evt *Event) Close() error {
  277. if evt.id == 0 {
  278. return os.ErrClosed
  279. }
  280. evt.id = 0
  281. runtime.SetFinalizer(evt, nil)
  282. pe := fmt.Sprintf("%s/%s", evt.group, evt.name)
  283. return removeEvent(evt.typ, pe)
  284. }
  285. func removeEvent(typ ProbeType, pe string) error {
  286. f, err := typ.eventsFile()
  287. if err != nil {
  288. return err
  289. }
  290. defer f.Close()
  291. // See [k,u]probe_events syntax above. The probe type does not need to be specified
  292. // for removals.
  293. if _, err = f.WriteString("-:" + pe); err != nil {
  294. return fmt.Errorf("remove event %q from %s: %w", pe, f.Name(), err)
  295. }
  296. return nil
  297. }
  298. // ID returns the tracefs ID associated with the event.
  299. func (evt *Event) ID() uint64 {
  300. return evt.id
  301. }
  302. // Group returns the tracefs group used by the event.
  303. func (evt *Event) Group() string {
  304. return evt.group
  305. }
  306. // KprobeToken creates the SYM[+offs] token for the tracefs api.
  307. func KprobeToken(args ProbeArgs) string {
  308. po := args.Symbol
  309. if args.Offset != 0 {
  310. po += fmt.Sprintf("+%#x", args.Offset)
  311. }
  312. return po
  313. }