diff --git a/link/kprobe.go b/link/kprobe.go index e2c79cb68..286e3c66a 100644 --- a/link/kprobe.go +++ b/link/kprobe.go @@ -429,7 +429,7 @@ func createTraceFSProbeEvent(typ probeType, args probeArgs) error { // the eBPF program itself. // See Documentation/kprobes.txt for more details. token = kprobeToken(args) - pe = fmt.Sprintf("%s:%s/%s %s", probePrefix(args.ret), args.group, sanitizedSymbol(args.symbol), token) + pe = fmt.Sprintf("%s:%s/%s %s", probePrefix(args.ret), args.group, sanitizeSymbol(args.symbol), token) case uprobeType: // The uprobe_events syntax is as follows: // p[:[GRP/]EVENT] PATH:OFFSET [FETCHARGS] : Set a probe @@ -481,7 +481,7 @@ func closeTraceFSProbeEvent(typ probeType, group, symbol string) error { // See [k,u]probe_events syntax above. The probe type does not need to be specified // for removals. - pe := fmt.Sprintf("-:%s/%s", group, sanitizedSymbol(symbol)) + pe := fmt.Sprintf("-:%s/%s", group, sanitizeSymbol(symbol)) if _, err = f.WriteString(pe); err != nil { return fmt.Errorf("writing '%s' to '%s': %w", pe, typ.EventsPath(), err) } diff --git a/link/perf_event.go b/link/perf_event.go index 84981b373..0e5bd4791 100644 --- a/link/perf_event.go +++ b/link/perf_event.go @@ -269,7 +269,7 @@ func unsafeStringPtr(str string) (unsafe.Pointer, error) { // name automatically has its invalid symbols converted to underscores so the caller // can pass a raw symbol name, e.g. a kernel symbol containing dots. func getTraceEventID(group, name string) (uint64, error) { - name = sanitizedSymbol(name) + name = sanitizeSymbol(name) tid, err := uint64FromFile(tracefsPath, "events", group, name, "id") if errors.Is(err, os.ErrNotExist) { return 0, fmt.Errorf("trace event %s/%s: %w", group, name, os.ErrNotExist) diff --git a/link/uprobe.go b/link/uprobe.go index 896d9e157..ca11f376b 100644 --- a/link/uprobe.go +++ b/link/uprobe.go @@ -6,7 +6,7 @@ import ( "fmt" "os" "path/filepath" - "regexp" + "strings" "sync" "github.com/cilium/ebpf" @@ -16,10 +16,6 @@ import ( var ( uprobeEventsPath = filepath.Join(tracefsPath, "uprobe_events") - // rgxEventSymbol is used to strip invalid characters from the [k,u]probe symbol - // as they are not allowed to be used as the EVENT token in tracefs. - rgxEventSymbol = regexp.MustCompile("[^a-zA-Z0-9]+") - uprobeRetprobeBit = struct { once sync.Once value uint64 @@ -296,7 +292,7 @@ func (ex *Executable) uprobe(symbol string, prog *ebpf.Program, opts *UprobeOpti } // Use tracefs if uprobe PMU is missing. - args.symbol = sanitizedSymbol(symbol) + args.symbol = sanitizeSymbol(symbol) tp, err = tracefsUprobe(args) if err != nil { return nil, fmt.Errorf("creating trace event '%s:%s' in tracefs: %w", ex.path, symbol, err) @@ -315,9 +311,29 @@ func tracefsUprobe(args probeArgs) (*perfEvent, error) { return tracefsProbe(uprobeType, args) } -// sanitizedSymbol replaces every invalid character for the tracefs api with an underscore. -func sanitizedSymbol(symbol string) string { - return rgxEventSymbol.ReplaceAllString(symbol, "_") +// sanitizeSymbol replaces every invalid character for the tracefs api with an underscore. +// It is equivalent to calling regexp.MustCompile("[^a-zA-Z0-9]+").ReplaceAllString("_"). +func sanitizeSymbol(s string) string { + var b strings.Builder + b.Grow(len(s)) + var skip bool + for _, c := range []byte(s) { + switch { + case c >= 'a' && c <= 'z', + c >= 'A' && c <= 'Z', + c >= '0' && c <= '9': + skip = false + b.WriteByte(c) + + default: + if !skip { + b.WriteByte('_') + skip = true + } + } + } + + return b.String() } // uprobeToken creates the PATH:OFFSET(REF_CTR_OFFSET) token for the tracefs api. diff --git a/link/uprobe_test.go b/link/uprobe_test.go index ae37894a6..e26c8e7b8 100644 --- a/link/uprobe_test.go +++ b/link/uprobe_test.go @@ -179,7 +179,7 @@ func TestUprobeTraceFS(t *testing.T) { // Prepare probe args. args := probeArgs{ - symbol: sanitizedSymbol(bashSym), + symbol: sanitizeSymbol(bashSym), path: bashEx.path, offset: off, pid: perfAllThreads, @@ -227,7 +227,7 @@ func TestUprobeCreateTraceFS(t *testing.T) { c.Assert(err, qt.IsNil) // Sanitize the symbol in order to be used in tracefs API. - ssym := sanitizedSymbol(bashSym) + ssym := sanitizeSymbol(bashSym) pg, _ := randomGroup("ebpftest") rg, _ := randomGroup("ebpftest") @@ -280,14 +280,16 @@ func TestUprobeSanitizedSymbol(t *testing.T) { expected string }{ {"readline", "readline"}, - {"main.Func", "main_Func"}, + {"main.Func123", "main_Func123"}, {"a.....a", "a_a"}, {"./;'{}[]a", "_a"}, + {"***xx**xx###", "_xx_xx_"}, + {`@P#r$i%v^3*+t)i&k++--`, "_P_r_i_v_3_t_i_k_"}, } for i, tt := range tests { t.Run(fmt.Sprint(i), func(t *testing.T) { - sanitized := sanitizedSymbol(tt.symbol) + sanitized := sanitizeSymbol(tt.symbol) if tt.expected != sanitized { t.Errorf("Expected sanitized symbol to be '%s', got '%s'", tt.expected, sanitized) }