Skip to content

Commit 46fb0a0

Browse files
Furistoroboquat
authored andcommitted
[agent-smith] Improve cpu and memory utilization
1 parent 8f5f7b6 commit 46fb0a0

File tree

4 files changed

+90
-40
lines changed

4 files changed

+90
-40
lines changed

components/ee/agent-smith/cmd/signature-matches.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ var signatureMatchesCmd = &cobra.Command{
2828
}
2929
defer f.Close()
3030

31+
sfc := classifier.SignatureReadCache{
32+
Reader: f,
33+
}
34+
3135
if cfgFile == "" {
3236
log.Info("no config present - reading signature from STDIN")
3337
var sig classifier.Signature
@@ -36,7 +40,7 @@ var signatureMatchesCmd = &cobra.Command{
3640
log.Fatal(err)
3741
}
3842

39-
match, err := sig.Matches(f)
43+
match, err := sig.Matches(&sfc)
4044
if err != nil {
4145
log.Fatal(err)
4246
}
@@ -60,7 +64,7 @@ var signatureMatchesCmd = &cobra.Command{
6064
var res []*classifier.Signature
6165
for _, bl := range cfg.Blocklists.Levels() {
6266
for _, s := range bl.Signatures {
63-
m, err := s.Matches(f)
67+
m, err := s.Matches(&sfc)
6468
if err != nil {
6569
log.WithError(err).WithField("signature", s.Name).Warn("cannot match signature")
6670
continue

components/ee/agent-smith/pkg/classifier/classifier.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package classifier
77
import (
88
"errors"
99
"fmt"
10+
"io"
1011
"os"
1112
"regexp"
1213
"strings"
@@ -196,8 +197,12 @@ func (sigcl *SignatureMatchClassifier) Matches(executable string, cmdline []stri
196197
defer r.Close()
197198

198199
var serr error
200+
201+
src := SignatureReadCache{
202+
Reader: r,
203+
}
199204
for _, sig := range sigcl.Signatures {
200-
match, err := sig.Matches(r)
205+
match, err := sig.Matches(&src)
201206
if match {
202207
sigcl.signatureHitTotal.Inc()
203208
return &Classification{
@@ -217,6 +222,13 @@ func (sigcl *SignatureMatchClassifier) Matches(executable string, cmdline []stri
217222
return sigNoMatch, nil
218223
}
219224

225+
type SignatureReadCache struct {
226+
Reader io.ReaderAt
227+
header []byte
228+
symbols []string
229+
rodata []byte
230+
}
231+
220232
func (sigcl *SignatureMatchClassifier) Describe(d chan<- *prometheus.Desc) {
221233
sigcl.processMissTotal.Describe(d)
222234
sigcl.signatureHitTotal.Describe(d)

components/ee/agent-smith/pkg/classifier/signature_test.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ func TestMatchELF(t *testing.T) {
2222
}
2323
defer input.Close()
2424

25+
sfc := SignatureReadCache{
26+
Reader: input,
27+
}
28+
2529
sig := Signature{
2630
Kind: ObjectELFSymbols,
2731
Pattern: []byte("bash_groupname_completion_function"),
@@ -32,7 +36,7 @@ func TestMatchELF(t *testing.T) {
3236
return
3337
}
3438

35-
matches, err := sig.Matches(input)
39+
matches, err := sig.Matches(&sfc)
3640
if err != nil {
3741
t.Errorf("cannot match signature: %v", err)
3842
return
@@ -60,7 +64,11 @@ func TestMatchAny(t *testing.T) {
6064
return
6165
}
6266

63-
matches, err := test.Signature.matchAny(bytes.NewReader(test.Input))
67+
sfc := SignatureReadCache{
68+
Reader: bytes.NewReader(test.Input),
69+
}
70+
71+
matches, err := test.Signature.matchAny(&sfc)
6472
if err != nil {
6573
t.Errorf("[%03d] cannot match signature: %v", i, err)
6674
return

components/ee/agent-smith/pkg/classifier/sinature.go

Lines changed: 61 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -103,16 +103,16 @@ func (s *Signature) Validate() error {
103103
}
104104

105105
// Matches checks if the signature applies to the stream
106-
func (s *Signature) Matches(in io.ReaderAt) (bool, error) {
106+
func (s *Signature) Matches(in *SignatureReadCache) (bool, error) {
107107
if s.Slice.Start > 0 {
108-
_, err := in.ReadAt([]byte{}, s.Slice.Start)
108+
_, err := in.Reader.ReadAt([]byte{}, s.Slice.Start)
109109
// slice start exceeds what we can read - this signature cannot match
110110
if err != nil {
111111
return false, nil
112112
}
113113
}
114114
if s.Slice.End > 0 {
115-
_, err := in.ReadAt([]byte{}, s.Slice.End)
115+
_, err := in.Reader.ReadAt([]byte{}, s.Slice.End)
116116
// slice start exceeds what we can read - this signature cannot match
117117
if err != nil {
118118
return false, nil
@@ -121,21 +121,27 @@ func (s *Signature) Matches(in io.ReaderAt) (bool, error) {
121121

122122
// check the object kind
123123
if s.Kind != ObjectAny {
124-
head := make([]byte, 261)
125-
_, err := in.ReadAt(head, 0)
126-
if err == io.EOF {
127-
// cannot read header which means that only Any rules would apply
128-
return false, nil
129-
}
130-
if err != nil {
131-
return false, xerrors.Errorf("cannot read stream head: %w", err)
124+
var head []byte
125+
if len(in.header) > 0 {
126+
head = in.header
127+
} else {
128+
head = make([]byte, 261)
129+
_, err := in.Reader.ReadAt(head, 0)
130+
if err == io.EOF {
131+
// cannot read header which means that only Any rules would apply
132+
return false, nil
133+
}
134+
if err != nil {
135+
return false, xerrors.Errorf("cannot read stream head: %w", err)
136+
}
137+
in.header = head
132138
}
133139

134140
matches := false
135141
switch s.Kind {
136142
case ObjectELFSymbols, ObjectELFRodata:
137143
matches = isELF(head)
138-
case ObjectAny:
144+
default:
139145
matches = true
140146
}
141147
if !matches {
@@ -172,16 +178,23 @@ func isELF(head []byte) bool {
172178
}
173179

174180
// matchELF matches a signature against an ELF file
175-
func (s *Signature) matchELFRodata(in io.ReaderAt) (bool, error) {
176-
executable, err := elf.NewFile(in)
177-
if err != nil {
178-
return false, xerrors.Errorf("cannot anaylse ELF file: %w", err)
179-
}
181+
func (s *Signature) matchELFRodata(in *SignatureReadCache) (bool, error) {
182+
var rodata []byte
183+
if len(in.rodata) > 0 {
184+
rodata = in.rodata
185+
} else {
186+
executable, err := elf.NewFile(in.Reader)
187+
if err != nil {
188+
return false, xerrors.Errorf("cannot anaylse ELF file: %w", err)
189+
}
180190

181-
rodata, err := ExtractELFRodata(executable)
182-
if err != nil {
183-
return false, err
191+
rodata, err = ExtractELFRodata(executable)
192+
if err != nil {
193+
return false, err
194+
}
195+
in.rodata = rodata
184196
}
197+
185198
matches, err := s.matches(rodata)
186199
if matches || err != nil {
187200
return matches, err
@@ -191,16 +204,23 @@ func (s *Signature) matchELFRodata(in io.ReaderAt) (bool, error) {
191204
}
192205

193206
// matchELF matches a signature against an ELF file
194-
func (s *Signature) matchELF(in io.ReaderAt) (bool, error) {
195-
executable, err := elf.NewFile(in)
196-
if err != nil {
197-
return false, xerrors.Errorf("cannot anaylse ELF file: %w", err)
198-
}
207+
func (s *Signature) matchELF(in *SignatureReadCache) (bool, error) {
208+
var symbols []string
209+
if len(in.symbols) > 0 {
210+
symbols = in.symbols
211+
} else {
212+
executable, err := elf.NewFile(in.Reader)
213+
if err != nil {
214+
return false, xerrors.Errorf("cannot anaylse ELF file: %w", err)
215+
}
199216

200-
symbols, err := ExtractELFSymbols(executable)
201-
if err != nil {
202-
return false, err
217+
symbols, err = ExtractELFSymbols(executable)
218+
if err != nil {
219+
return false, err
220+
}
221+
in.symbols = symbols
203222
}
223+
204224
for _, sym := range symbols {
205225
matches, err := s.matches([]byte(sym))
206226
if matches || err != nil {
@@ -213,22 +233,28 @@ func (s *Signature) matchELF(in io.ReaderAt) (bool, error) {
213233

214234
// ExtractELFSymbols extracts all ELF symbol names from an ELF binary
215235
func ExtractELFSymbols(executable *elf.File) ([]string, error) {
216-
var symbols []string
217236
syms, err := executable.Symbols()
218237
if err != nil && err != elf.ErrNoSymbols {
219238
return nil, xerrors.Errorf("cannot get dynsym section: %w", err)
220239
}
221-
for _, s := range syms {
222-
symbols = append(symbols, s.Name)
223-
}
224240

225241
dynsyms, err := executable.DynamicSymbols()
226242
if err != nil && err != elf.ErrNoSymbols {
227243
return nil, xerrors.Errorf("cannot get dynsym section: %w", err)
228244
}
245+
246+
symbols := make([]string, len(syms)+len(dynsyms))
247+
i := 0
248+
for _, s := range syms {
249+
symbols[i] = s.Name
250+
i += 1
251+
}
252+
229253
for _, s := range dynsyms {
230-
symbols = append(symbols, s.Name)
254+
symbols[i] = s.Name
255+
i += 1
231256
}
257+
232258
return symbols, nil
233259
}
234260

@@ -247,11 +273,11 @@ func ExtractELFRodata(executable *elf.File) ([]byte, error) {
247273
}
248274

249275
// matchAny matches a signature against a binary file
250-
func (s *Signature) matchAny(in io.ReaderAt) (bool, error) {
276+
func (s *Signature) matchAny(in *SignatureReadCache) (bool, error) {
251277
buffer := make([]byte, 8096)
252278
pos := s.Slice.Start
253279
for {
254-
n, err := in.ReadAt(buffer, pos)
280+
n, err := in.Reader.ReadAt(buffer, pos)
255281
sub := buffer[0:n]
256282
pos += int64(n)
257283

0 commit comments

Comments
 (0)