-
Notifications
You must be signed in to change notification settings - Fork 1
/
symbols_loaded.go
239 lines (207 loc) · 6.76 KB
/
symbols_loaded.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
package derive
import (
"path"
"strings"
lru "github.com/hashicorp/golang-lru"
"golang.org/x/exp/maps"
"github.com/nextlinux/tracee/pkg/errfmt"
"github.com/nextlinux/tracee/pkg/events"
"github.com/nextlinux/tracee/pkg/events/parse"
"github.com/nextlinux/tracee/pkg/filters"
"github.com/nextlinux/tracee/pkg/logger"
"github.com/nextlinux/tracee/pkg/policy"
"github.com/nextlinux/tracee/pkg/utils/sharedobjs"
"github.com/nextlinux/tracee/types/trace"
)
func SymbolsLoaded(
soLoader sharedobjs.DynamicSymbolsLoader,
policies *policy.Policies,
) DeriveFunction {
symbolsLoadedFilters := map[string]filters.Filter{}
for p := range policies.Map() {
f := p.ArgFilter.GetEventFilters(events.SymbolsLoaded)
maps.Copy(symbolsLoadedFilters, f)
}
loadWatchedSymbols := []string{}
loadWhitelistedLibs := []string{}
if len(symbolsLoadedFilters) > 0 {
watchedSymbolsFilter, ok := symbolsLoadedFilters["symbols"].(*filters.StringFilter)
if watchedSymbolsFilter != nil && ok {
loadWatchedSymbols = watchedSymbolsFilter.Equal()
}
whitelistedLibsFilter, ok := symbolsLoadedFilters["library_path"].(*filters.StringFilter)
if whitelistedLibsFilter != nil && ok {
loadWhitelistedLibs = whitelistedLibsFilter.NotEqual()
}
}
gen := initSymbolsLoadedEventGenerator(soLoader, loadWatchedSymbols, loadWhitelistedLibs)
return deriveSingleEvent(events.SymbolsLoaded, gen.deriveArgs)
}
// Most specific paths should be at the top, to prevent bugs with iterations over the list
var knownLibrariesDirs = []string{
"/usr/lib64/",
"/usr/lib/",
"/lib64/",
"/lib/",
}
var knownArchitectureDirs = []string{
"x86_64-linux-gnu",
"aarch64-linux-gnu",
"i386-linux-gnu",
"i686-linux-gnu",
"", // non-specific architecture dir
}
// symbolsLoadedEventGenerator is responsible of generating event if shared object loaded to a
// process export one or more from given watched symbols.
type symbolsLoadedEventGenerator struct {
soLoader sharedobjs.DynamicSymbolsLoader
watchedSymbols map[string]bool
pathPrefixWhitelist []string
librariesWhitelist []string
returnedErrors map[string]bool
libsCache *lru.Cache
}
func initSymbolsLoadedEventGenerator(
soLoader sharedobjs.DynamicSymbolsLoader,
watchedSymbols []string,
whitelistedLibsPrefixes []string,
) *symbolsLoadedEventGenerator {
watchedSymbolsMap := make(map[string]bool)
for _, sym := range watchedSymbols {
watchedSymbolsMap[sym] = true
}
var libraries, prefixes []string
for _, wlPath := range whitelistedLibsPrefixes {
if strings.HasPrefix(wlPath, "/") {
prefixes = append(prefixes, wlPath)
} else {
libraries = append(libraries, wlPath)
}
}
cacheLRU, _ := lru.New(10240)
return &symbolsLoadedEventGenerator{
soLoader: soLoader,
watchedSymbols: watchedSymbolsMap,
pathPrefixWhitelist: prefixes,
librariesWhitelist: libraries,
returnedErrors: make(map[string]bool),
libsCache: cacheLRU,
}
}
func (symbsLoadedGen *symbolsLoadedEventGenerator) deriveArgs(
event trace.Event,
) (
[]interface{}, error,
) {
loadingObjectInfo, err := getSharedObjectInfo(event)
if err != nil {
return nil, errfmt.WrapError(err)
}
if symbsLoadedGen.isWhitelist(loadingObjectInfo.Path) {
return nil, nil
}
matchedSyms, ok := symbsLoadedGen.getSymbolsFromCache(loadingObjectInfo.Id)
if ok {
if len(matchedSyms) > 0 {
return []interface{}{loadingObjectInfo.Path, matchedSyms}, nil
}
return nil, nil
}
soSyms, err := symbsLoadedGen.soLoader.GetExportedSymbols(loadingObjectInfo)
// This error happens frequently in some environments, so we need to silence it to reduce spam.
// Either way, this is not a critical error so we don't return it.
if err != nil {
// TODO: rate limit frequent errors for overloaded envs
_, ok := symbsLoadedGen.returnedErrors[err.Error()]
if !ok {
symbsLoadedGen.returnedErrors[err.Error()] = true
logger.Warnw("symbols_loaded", "object loaded", loadingObjectInfo, "error", err.Error())
} else {
logger.Debugw("symbols_loaded", "object loaded", loadingObjectInfo, "error", err.Error())
}
return nil, nil
}
var exportedWatchSymbols []string
for sym := range soSyms {
if symbsLoadedGen.watchedSymbols[sym] {
exportedWatchSymbols = append(exportedWatchSymbols, sym)
}
}
symbsLoadedGen.libsCache.Add(loadingObjectInfo.Id, exportedWatchSymbols)
if len(exportedWatchSymbols) > 0 {
return []interface{}{loadingObjectInfo.Path, exportedWatchSymbols}, nil
}
return nil, nil
}
// isWhitelist check if a SO's path is in the whitelist given in initialization
func (symbsLoadedGen *symbolsLoadedEventGenerator) isWhitelist(soPath string) bool {
// Check absolute path libraries whitelist
for _, prefix := range symbsLoadedGen.pathPrefixWhitelist {
if strings.HasPrefix(soPath, prefix) {
return true
}
}
// Check if SO is whitelisted library which resides in one of the known libs paths
if len(symbsLoadedGen.librariesWhitelist) > 0 {
for _, libsDirectory := range knownLibrariesDirs {
if strings.HasPrefix(soPath, libsDirectory) {
for _, archDir := range knownArchitectureDirs {
archLibDir := path.Join(libsDirectory, archDir)
if strings.HasPrefix(soPath, archLibDir) {
for _, wlLib := range symbsLoadedGen.librariesWhitelist {
if strings.HasPrefix(soPath, path.Join(archLibDir, wlLib)) {
return true
}
}
break
}
}
break
}
}
}
return false
}
// getSymbolsFromCache query the cache for check results of specified object.
// Return the watched symbols found in the object, and if it was found in the cache.
func (symbsLoadedGen *symbolsLoadedEventGenerator) getSymbolsFromCache(id sharedobjs.ObjID) ([]string, bool) {
cachedSyms, ok := symbsLoadedGen.libsCache.Get(id)
if !ok {
return nil, false
}
cachedSymsList, ok := cachedSyms.([]string)
if !ok {
logger.Errorw("Unexpected cached type")
return nil, false
}
return cachedSymsList, true
}
// getSharedObjectInfo extract from SO loading event the information available about the SO
func getSharedObjectInfo(event trace.Event) (sharedobjs.ObjInfo, error) {
var objInfo sharedobjs.ObjInfo
loadedObjectInode, err := parse.ArgVal[uint64](&event, "inode")
if err != nil {
return objInfo, errfmt.WrapError(err)
}
loadedObjectDevice, err := parse.ArgVal[uint32](&event, "dev")
if err != nil {
return objInfo, errfmt.WrapError(err)
}
loadedObjectCtime, err := parse.ArgVal[uint64](&event, "ctime")
if err != nil {
return objInfo, errfmt.WrapError(err)
}
loadedObjectPath, err := parse.ArgVal[string](&event, "pathname")
if err != nil {
return objInfo, errfmt.WrapError(err)
}
objInfo = sharedobjs.ObjInfo{
Id: sharedobjs.ObjID{
Inode: loadedObjectInode,
Device: loadedObjectDevice,
Ctime: loadedObjectCtime},
Path: loadedObjectPath,
MountNS: event.MountNS,
}
return objInfo, nil
}